10b57cec5SDimitry Andric //==- CheckObjCDealloc.cpp - Check ObjC -dealloc implementation --*- C++ -*-==// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // 90b57cec5SDimitry Andric // This checker analyzes Objective-C -dealloc methods and their callees 100b57cec5SDimitry Andric // to warn about improper releasing of instance variables that back synthesized 110b57cec5SDimitry Andric // properties. It warns about missing releases in the following cases: 120b57cec5SDimitry Andric // - When a class has a synthesized instance variable for a 'retain' or 'copy' 130b57cec5SDimitry Andric // property and lacks a -dealloc method in its implementation. 140b57cec5SDimitry Andric // - When a class has a synthesized instance variable for a 'retain'/'copy' 150b57cec5SDimitry Andric // property but the ivar is not released in -dealloc by either -release 160b57cec5SDimitry Andric // or by nilling out the property. 170b57cec5SDimitry Andric // 180b57cec5SDimitry Andric // It warns about extra releases in -dealloc (but not in callees) when a 190b57cec5SDimitry Andric // synthesized instance variable is released in the following cases: 200b57cec5SDimitry Andric // - When the property is 'assign' and is not 'readonly'. 210b57cec5SDimitry Andric // - When the property is 'weak'. 220b57cec5SDimitry Andric // 230b57cec5SDimitry Andric // This checker only warns for instance variables synthesized to back 240b57cec5SDimitry Andric // properties. Handling the more general case would require inferring whether 250b57cec5SDimitry Andric // an instance variable is stored retained or not. For synthesized properties, 260b57cec5SDimitry Andric // this is specified in the property declaration itself. 270b57cec5SDimitry Andric // 280b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 290b57cec5SDimitry Andric 300b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 31a7dea167SDimitry Andric #include "clang/Analysis/PathDiagnostic.h" 320b57cec5SDimitry Andric #include "clang/AST/Attr.h" 330b57cec5SDimitry Andric #include "clang/AST/DeclObjC.h" 340b57cec5SDimitry Andric #include "clang/AST/Expr.h" 350b57cec5SDimitry Andric #include "clang/AST/ExprObjC.h" 360b57cec5SDimitry Andric #include "clang/Basic/LangOptions.h" 370b57cec5SDimitry Andric #include "clang/Basic/TargetInfo.h" 380b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 390b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 400b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h" 410b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" 420b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 430b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 440b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" 450b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" 460b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" 470b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h" 48bdd1243dSDimitry Andric #include <optional> 490b57cec5SDimitry Andric 500b57cec5SDimitry Andric using namespace clang; 510b57cec5SDimitry Andric using namespace ento; 520b57cec5SDimitry Andric 530b57cec5SDimitry Andric /// Indicates whether an instance variable is required to be released in 540b57cec5SDimitry Andric /// -dealloc. 550b57cec5SDimitry Andric enum class ReleaseRequirement { 560b57cec5SDimitry Andric /// The instance variable must be released, either by calling 570b57cec5SDimitry Andric /// -release on it directly or by nilling it out with a property setter. 580b57cec5SDimitry Andric MustRelease, 590b57cec5SDimitry Andric 600b57cec5SDimitry Andric /// The instance variable must not be directly released with -release. 610b57cec5SDimitry Andric MustNotReleaseDirectly, 620b57cec5SDimitry Andric 630b57cec5SDimitry Andric /// The requirement for the instance variable could not be determined. 640b57cec5SDimitry Andric Unknown 650b57cec5SDimitry Andric }; 660b57cec5SDimitry Andric 670b57cec5SDimitry Andric /// Returns true if the property implementation is synthesized and the 680b57cec5SDimitry Andric /// type of the property is retainable. 690b57cec5SDimitry Andric static bool isSynthesizedRetainableProperty(const ObjCPropertyImplDecl *I, 700b57cec5SDimitry Andric const ObjCIvarDecl **ID, 710b57cec5SDimitry Andric const ObjCPropertyDecl **PD) { 720b57cec5SDimitry Andric 730b57cec5SDimitry Andric if (I->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize) 740b57cec5SDimitry Andric return false; 750b57cec5SDimitry Andric 760b57cec5SDimitry Andric (*ID) = I->getPropertyIvarDecl(); 770b57cec5SDimitry Andric if (!(*ID)) 780b57cec5SDimitry Andric return false; 790b57cec5SDimitry Andric 800b57cec5SDimitry Andric QualType T = (*ID)->getType(); 810b57cec5SDimitry Andric if (!T->isObjCRetainableType()) 820b57cec5SDimitry Andric return false; 830b57cec5SDimitry Andric 840b57cec5SDimitry Andric (*PD) = I->getPropertyDecl(); 850b57cec5SDimitry Andric // Shouldn't be able to synthesize a property that doesn't exist. 860b57cec5SDimitry Andric assert(*PD); 870b57cec5SDimitry Andric 880b57cec5SDimitry Andric return true; 890b57cec5SDimitry Andric } 900b57cec5SDimitry Andric 910b57cec5SDimitry Andric namespace { 920b57cec5SDimitry Andric 930b57cec5SDimitry Andric class ObjCDeallocChecker 940b57cec5SDimitry Andric : public Checker<check::ASTDecl<ObjCImplementationDecl>, 950b57cec5SDimitry Andric check::PreObjCMessage, check::PostObjCMessage, 960b57cec5SDimitry Andric check::PreCall, 970b57cec5SDimitry Andric check::BeginFunction, check::EndFunction, 980b57cec5SDimitry Andric eval::Assume, 990b57cec5SDimitry Andric check::PointerEscape, 1000b57cec5SDimitry Andric check::PreStmt<ReturnStmt>> { 1010b57cec5SDimitry Andric 102647cbc5dSDimitry Andric mutable const IdentifierInfo *NSObjectII = nullptr; 103647cbc5dSDimitry Andric mutable const IdentifierInfo *SenTestCaseII = nullptr; 104647cbc5dSDimitry Andric mutable const IdentifierInfo *XCTestCaseII = nullptr; 105647cbc5dSDimitry Andric mutable const IdentifierInfo *Block_releaseII = nullptr; 106647cbc5dSDimitry Andric mutable const IdentifierInfo *CIFilterII = nullptr; 1070b57cec5SDimitry Andric 108647cbc5dSDimitry Andric mutable Selector DeallocSel; 109647cbc5dSDimitry Andric mutable Selector ReleaseSel; 1100b57cec5SDimitry Andric 111647cbc5dSDimitry Andric const BugType MissingReleaseBugType{this, "Missing ivar release (leak)", 112647cbc5dSDimitry Andric categories::MemoryRefCount}; 113647cbc5dSDimitry Andric const BugType ExtraReleaseBugType{this, "Extra ivar release", 114647cbc5dSDimitry Andric categories::MemoryRefCount}; 115647cbc5dSDimitry Andric const BugType MistakenDeallocBugType{this, "Mistaken dealloc", 116647cbc5dSDimitry Andric categories::MemoryRefCount}; 1170b57cec5SDimitry Andric 1180b57cec5SDimitry Andric public: 1190b57cec5SDimitry Andric void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr, 1200b57cec5SDimitry Andric BugReporter &BR) const; 1210b57cec5SDimitry Andric void checkBeginFunction(CheckerContext &Ctx) const; 1220b57cec5SDimitry Andric void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; 1230b57cec5SDimitry Andric void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 1240b57cec5SDimitry Andric void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; 1250b57cec5SDimitry Andric 1260b57cec5SDimitry Andric ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond, 1270b57cec5SDimitry Andric bool Assumption) const; 1280b57cec5SDimitry Andric 1290b57cec5SDimitry Andric ProgramStateRef checkPointerEscape(ProgramStateRef State, 1300b57cec5SDimitry Andric const InvalidatedSymbols &Escaped, 1310b57cec5SDimitry Andric const CallEvent *Call, 1320b57cec5SDimitry Andric PointerEscapeKind Kind) const; 1330b57cec5SDimitry Andric void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const; 1340b57cec5SDimitry Andric void checkEndFunction(const ReturnStmt *RS, CheckerContext &Ctx) const; 1350b57cec5SDimitry Andric 1360b57cec5SDimitry Andric private: 1370b57cec5SDimitry Andric void diagnoseMissingReleases(CheckerContext &C) const; 1380b57cec5SDimitry Andric 1390b57cec5SDimitry Andric bool diagnoseExtraRelease(SymbolRef ReleasedValue, const ObjCMethodCall &M, 1400b57cec5SDimitry Andric CheckerContext &C) const; 1410b57cec5SDimitry Andric 1420b57cec5SDimitry Andric bool diagnoseMistakenDealloc(SymbolRef DeallocedValue, 1430b57cec5SDimitry Andric const ObjCMethodCall &M, 1440b57cec5SDimitry Andric CheckerContext &C) const; 1450b57cec5SDimitry Andric 1460b57cec5SDimitry Andric SymbolRef getValueReleasedByNillingOut(const ObjCMethodCall &M, 1470b57cec5SDimitry Andric CheckerContext &C) const; 1480b57cec5SDimitry Andric 1490b57cec5SDimitry Andric const ObjCIvarRegion *getIvarRegionForIvarSymbol(SymbolRef IvarSym) const; 1500b57cec5SDimitry Andric SymbolRef getInstanceSymbolFromIvarSymbol(SymbolRef IvarSym) const; 1510b57cec5SDimitry Andric 1520b57cec5SDimitry Andric const ObjCPropertyImplDecl* 1530b57cec5SDimitry Andric findPropertyOnDeallocatingInstance(SymbolRef IvarSym, 1540b57cec5SDimitry Andric CheckerContext &C) const; 1550b57cec5SDimitry Andric 1560b57cec5SDimitry Andric ReleaseRequirement 1570b57cec5SDimitry Andric getDeallocReleaseRequirement(const ObjCPropertyImplDecl *PropImpl) const; 1580b57cec5SDimitry Andric 1590b57cec5SDimitry Andric bool isInInstanceDealloc(const CheckerContext &C, SVal &SelfValOut) const; 1600b57cec5SDimitry Andric bool isInInstanceDealloc(const CheckerContext &C, const LocationContext *LCtx, 1610b57cec5SDimitry Andric SVal &SelfValOut) const; 1620b57cec5SDimitry Andric bool instanceDeallocIsOnStack(const CheckerContext &C, 1630b57cec5SDimitry Andric SVal &InstanceValOut) const; 1640b57cec5SDimitry Andric 1650b57cec5SDimitry Andric bool isSuperDeallocMessage(const ObjCMethodCall &M) const; 1660b57cec5SDimitry Andric 1670b57cec5SDimitry Andric const ObjCImplDecl *getContainingObjCImpl(const LocationContext *LCtx) const; 1680b57cec5SDimitry Andric 1690b57cec5SDimitry Andric const ObjCPropertyDecl * 1700b57cec5SDimitry Andric findShadowedPropertyDecl(const ObjCPropertyImplDecl *PropImpl) const; 1710b57cec5SDimitry Andric 1720b57cec5SDimitry Andric void transitionToReleaseValue(CheckerContext &C, SymbolRef Value) const; 1730b57cec5SDimitry Andric ProgramStateRef removeValueRequiringRelease(ProgramStateRef State, 1740b57cec5SDimitry Andric SymbolRef InstanceSym, 1750b57cec5SDimitry Andric SymbolRef ValueSym) const; 1760b57cec5SDimitry Andric 1770b57cec5SDimitry Andric void initIdentifierInfoAndSelectors(ASTContext &Ctx) const; 1780b57cec5SDimitry Andric 1790b57cec5SDimitry Andric bool classHasSeparateTeardown(const ObjCInterfaceDecl *ID) const; 1800b57cec5SDimitry Andric 1810b57cec5SDimitry Andric bool isReleasedByCIFilterDealloc(const ObjCPropertyImplDecl *PropImpl) const; 1820b57cec5SDimitry Andric bool isNibLoadedIvarWithoutRetain(const ObjCPropertyImplDecl *PropImpl) const; 1830b57cec5SDimitry Andric }; 1840b57cec5SDimitry Andric } // End anonymous namespace. 1850b57cec5SDimitry Andric 1860b57cec5SDimitry Andric 1870b57cec5SDimitry Andric /// Maps from the symbol for a class instance to the set of 1880b57cec5SDimitry Andric /// symbols remaining that must be released in -dealloc. 1890b57cec5SDimitry Andric REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(SymbolSet, SymbolRef) 1900b57cec5SDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(UnreleasedIvarMap, SymbolRef, SymbolSet) 1910b57cec5SDimitry Andric 1920b57cec5SDimitry Andric 1930b57cec5SDimitry Andric /// An AST check that diagnose when the class requires a -dealloc method and 1940b57cec5SDimitry Andric /// is missing one. 1950b57cec5SDimitry Andric void ObjCDeallocChecker::checkASTDecl(const ObjCImplementationDecl *D, 1960b57cec5SDimitry Andric AnalysisManager &Mgr, 1970b57cec5SDimitry Andric BugReporter &BR) const { 1980b57cec5SDimitry Andric assert(Mgr.getLangOpts().getGC() != LangOptions::GCOnly); 1990b57cec5SDimitry Andric assert(!Mgr.getLangOpts().ObjCAutoRefCount); 2000b57cec5SDimitry Andric initIdentifierInfoAndSelectors(Mgr.getASTContext()); 2010b57cec5SDimitry Andric 2020b57cec5SDimitry Andric const ObjCInterfaceDecl *ID = D->getClassInterface(); 2030b57cec5SDimitry Andric // If the class is known to have a lifecycle with a separate teardown method 2040b57cec5SDimitry Andric // then it may not require a -dealloc method. 2050b57cec5SDimitry Andric if (classHasSeparateTeardown(ID)) 2060b57cec5SDimitry Andric return; 2070b57cec5SDimitry Andric 2080b57cec5SDimitry Andric // Does the class contain any synthesized properties that are retainable? 2090b57cec5SDimitry Andric // If not, skip the check entirely. 2100b57cec5SDimitry Andric const ObjCPropertyImplDecl *PropImplRequiringRelease = nullptr; 2110b57cec5SDimitry Andric bool HasOthers = false; 2120b57cec5SDimitry Andric for (const auto *I : D->property_impls()) { 2130b57cec5SDimitry Andric if (getDeallocReleaseRequirement(I) == ReleaseRequirement::MustRelease) { 2140b57cec5SDimitry Andric if (!PropImplRequiringRelease) 2150b57cec5SDimitry Andric PropImplRequiringRelease = I; 2160b57cec5SDimitry Andric else { 2170b57cec5SDimitry Andric HasOthers = true; 2180b57cec5SDimitry Andric break; 2190b57cec5SDimitry Andric } 2200b57cec5SDimitry Andric } 2210b57cec5SDimitry Andric } 2220b57cec5SDimitry Andric 2230b57cec5SDimitry Andric if (!PropImplRequiringRelease) 2240b57cec5SDimitry Andric return; 2250b57cec5SDimitry Andric 2260b57cec5SDimitry Andric const ObjCMethodDecl *MD = nullptr; 2270b57cec5SDimitry Andric 2280b57cec5SDimitry Andric // Scan the instance methods for "dealloc". 2290b57cec5SDimitry Andric for (const auto *I : D->instance_methods()) { 2300b57cec5SDimitry Andric if (I->getSelector() == DeallocSel) { 2310b57cec5SDimitry Andric MD = I; 2320b57cec5SDimitry Andric break; 2330b57cec5SDimitry Andric } 2340b57cec5SDimitry Andric } 2350b57cec5SDimitry Andric 2360b57cec5SDimitry Andric if (!MD) { // No dealloc found. 2370b57cec5SDimitry Andric const char* Name = "Missing -dealloc"; 2380b57cec5SDimitry Andric 2390b57cec5SDimitry Andric std::string Buf; 2400b57cec5SDimitry Andric llvm::raw_string_ostream OS(Buf); 2410b57cec5SDimitry Andric OS << "'" << *D << "' lacks a 'dealloc' instance method but " 2420b57cec5SDimitry Andric << "must release '" << *PropImplRequiringRelease->getPropertyIvarDecl() 2430b57cec5SDimitry Andric << "'"; 2440b57cec5SDimitry Andric 2450b57cec5SDimitry Andric if (HasOthers) 2460b57cec5SDimitry Andric OS << " and others"; 2470b57cec5SDimitry Andric PathDiagnosticLocation DLoc = 2480b57cec5SDimitry Andric PathDiagnosticLocation::createBegin(D, BR.getSourceManager()); 2490b57cec5SDimitry Andric 250*0fca6ea1SDimitry Andric BR.EmitBasicReport(D, this, Name, categories::CoreFoundationObjectiveC, Buf, 251*0fca6ea1SDimitry Andric DLoc); 2520b57cec5SDimitry Andric return; 2530b57cec5SDimitry Andric } 2540b57cec5SDimitry Andric } 2550b57cec5SDimitry Andric 2560b57cec5SDimitry Andric /// If this is the beginning of -dealloc, mark the values initially stored in 2570b57cec5SDimitry Andric /// instance variables that must be released by the end of -dealloc 2580b57cec5SDimitry Andric /// as unreleased in the state. 2590b57cec5SDimitry Andric void ObjCDeallocChecker::checkBeginFunction( 2600b57cec5SDimitry Andric CheckerContext &C) const { 2610b57cec5SDimitry Andric initIdentifierInfoAndSelectors(C.getASTContext()); 2620b57cec5SDimitry Andric 2630b57cec5SDimitry Andric // Only do this if the current method is -dealloc. 2640b57cec5SDimitry Andric SVal SelfVal; 2650b57cec5SDimitry Andric if (!isInInstanceDealloc(C, SelfVal)) 2660b57cec5SDimitry Andric return; 2670b57cec5SDimitry Andric 2680b57cec5SDimitry Andric SymbolRef SelfSymbol = SelfVal.getAsSymbol(); 2690b57cec5SDimitry Andric 2700b57cec5SDimitry Andric const LocationContext *LCtx = C.getLocationContext(); 2710b57cec5SDimitry Andric ProgramStateRef InitialState = C.getState(); 2720b57cec5SDimitry Andric 2730b57cec5SDimitry Andric ProgramStateRef State = InitialState; 2740b57cec5SDimitry Andric 2750b57cec5SDimitry Andric SymbolSet::Factory &F = State->getStateManager().get_context<SymbolSet>(); 2760b57cec5SDimitry Andric 2770b57cec5SDimitry Andric // Symbols that must be released by the end of the -dealloc; 2780b57cec5SDimitry Andric SymbolSet RequiredReleases = F.getEmptySet(); 2790b57cec5SDimitry Andric 2800b57cec5SDimitry Andric // If we're an inlined -dealloc, we should add our symbols to the existing 2810b57cec5SDimitry Andric // set from our subclass. 2820b57cec5SDimitry Andric if (const SymbolSet *CurrSet = State->get<UnreleasedIvarMap>(SelfSymbol)) 2830b57cec5SDimitry Andric RequiredReleases = *CurrSet; 2840b57cec5SDimitry Andric 2850b57cec5SDimitry Andric for (auto *PropImpl : getContainingObjCImpl(LCtx)->property_impls()) { 2860b57cec5SDimitry Andric ReleaseRequirement Requirement = getDeallocReleaseRequirement(PropImpl); 2870b57cec5SDimitry Andric if (Requirement != ReleaseRequirement::MustRelease) 2880b57cec5SDimitry Andric continue; 2890b57cec5SDimitry Andric 2900b57cec5SDimitry Andric SVal LVal = State->getLValue(PropImpl->getPropertyIvarDecl(), SelfVal); 291bdd1243dSDimitry Andric std::optional<Loc> LValLoc = LVal.getAs<Loc>(); 2920b57cec5SDimitry Andric if (!LValLoc) 2930b57cec5SDimitry Andric continue; 2940b57cec5SDimitry Andric 29581ad6265SDimitry Andric SVal InitialVal = State->getSVal(*LValLoc); 2960b57cec5SDimitry Andric SymbolRef Symbol = InitialVal.getAsSymbol(); 2970b57cec5SDimitry Andric if (!Symbol || !isa<SymbolRegionValue>(Symbol)) 2980b57cec5SDimitry Andric continue; 2990b57cec5SDimitry Andric 3000b57cec5SDimitry Andric // Mark the value as requiring a release. 3010b57cec5SDimitry Andric RequiredReleases = F.add(RequiredReleases, Symbol); 3020b57cec5SDimitry Andric } 3030b57cec5SDimitry Andric 3040b57cec5SDimitry Andric if (!RequiredReleases.isEmpty()) { 3050b57cec5SDimitry Andric State = State->set<UnreleasedIvarMap>(SelfSymbol, RequiredReleases); 3060b57cec5SDimitry Andric } 3070b57cec5SDimitry Andric 3080b57cec5SDimitry Andric if (State != InitialState) { 3090b57cec5SDimitry Andric C.addTransition(State); 3100b57cec5SDimitry Andric } 3110b57cec5SDimitry Andric } 3120b57cec5SDimitry Andric 3130b57cec5SDimitry Andric /// Given a symbol for an ivar, return the ivar region it was loaded from. 3140b57cec5SDimitry Andric /// Returns nullptr if the instance symbol cannot be found. 3150b57cec5SDimitry Andric const ObjCIvarRegion * 3160b57cec5SDimitry Andric ObjCDeallocChecker::getIvarRegionForIvarSymbol(SymbolRef IvarSym) const { 3170b57cec5SDimitry Andric return dyn_cast_or_null<ObjCIvarRegion>(IvarSym->getOriginRegion()); 3180b57cec5SDimitry Andric } 3190b57cec5SDimitry Andric 3200b57cec5SDimitry Andric /// Given a symbol for an ivar, return a symbol for the instance containing 3210b57cec5SDimitry Andric /// the ivar. Returns nullptr if the instance symbol cannot be found. 3220b57cec5SDimitry Andric SymbolRef 3230b57cec5SDimitry Andric ObjCDeallocChecker::getInstanceSymbolFromIvarSymbol(SymbolRef IvarSym) const { 3240b57cec5SDimitry Andric 3250b57cec5SDimitry Andric const ObjCIvarRegion *IvarRegion = getIvarRegionForIvarSymbol(IvarSym); 3260b57cec5SDimitry Andric if (!IvarRegion) 3270b57cec5SDimitry Andric return nullptr; 3280b57cec5SDimitry Andric 32906c3fb27SDimitry Andric const SymbolicRegion *SR = IvarRegion->getSymbolicBase(); 33006c3fb27SDimitry Andric assert(SR && "Symbolic base should not be nullptr"); 33106c3fb27SDimitry Andric return SR->getSymbol(); 3320b57cec5SDimitry Andric } 3330b57cec5SDimitry Andric 3340b57cec5SDimitry Andric /// If we are in -dealloc or -dealloc is on the stack, handle the call if it is 3350b57cec5SDimitry Andric /// a release or a nilling-out property setter. 3360b57cec5SDimitry Andric void ObjCDeallocChecker::checkPreObjCMessage( 3370b57cec5SDimitry Andric const ObjCMethodCall &M, CheckerContext &C) const { 3380b57cec5SDimitry Andric // Only run if -dealloc is on the stack. 3390b57cec5SDimitry Andric SVal DeallocedInstance; 3400b57cec5SDimitry Andric if (!instanceDeallocIsOnStack(C, DeallocedInstance)) 3410b57cec5SDimitry Andric return; 3420b57cec5SDimitry Andric 3430b57cec5SDimitry Andric SymbolRef ReleasedValue = nullptr; 3440b57cec5SDimitry Andric 3450b57cec5SDimitry Andric if (M.getSelector() == ReleaseSel) { 3460b57cec5SDimitry Andric ReleasedValue = M.getReceiverSVal().getAsSymbol(); 3470b57cec5SDimitry Andric } else if (M.getSelector() == DeallocSel && !M.isReceiverSelfOrSuper()) { 3480b57cec5SDimitry Andric if (diagnoseMistakenDealloc(M.getReceiverSVal().getAsSymbol(), M, C)) 3490b57cec5SDimitry Andric return; 3500b57cec5SDimitry Andric } 3510b57cec5SDimitry Andric 3520b57cec5SDimitry Andric if (ReleasedValue) { 3530b57cec5SDimitry Andric // An instance variable symbol was released with -release: 3540b57cec5SDimitry Andric // [_property release]; 3550b57cec5SDimitry Andric if (diagnoseExtraRelease(ReleasedValue,M, C)) 3560b57cec5SDimitry Andric return; 3570b57cec5SDimitry Andric } else { 3580b57cec5SDimitry Andric // An instance variable symbol was released nilling out its property: 3590b57cec5SDimitry Andric // self.property = nil; 3600b57cec5SDimitry Andric ReleasedValue = getValueReleasedByNillingOut(M, C); 3610b57cec5SDimitry Andric } 3620b57cec5SDimitry Andric 3630b57cec5SDimitry Andric if (!ReleasedValue) 3640b57cec5SDimitry Andric return; 3650b57cec5SDimitry Andric 3660b57cec5SDimitry Andric transitionToReleaseValue(C, ReleasedValue); 3670b57cec5SDimitry Andric } 3680b57cec5SDimitry Andric 3690b57cec5SDimitry Andric /// If we are in -dealloc or -dealloc is on the stack, handle the call if it is 3700b57cec5SDimitry Andric /// call to Block_release(). 3710b57cec5SDimitry Andric void ObjCDeallocChecker::checkPreCall(const CallEvent &Call, 3720b57cec5SDimitry Andric CheckerContext &C) const { 3730b57cec5SDimitry Andric const IdentifierInfo *II = Call.getCalleeIdentifier(); 3740b57cec5SDimitry Andric if (II != Block_releaseII) 3750b57cec5SDimitry Andric return; 3760b57cec5SDimitry Andric 3770b57cec5SDimitry Andric if (Call.getNumArgs() != 1) 3780b57cec5SDimitry Andric return; 3790b57cec5SDimitry Andric 3800b57cec5SDimitry Andric SymbolRef ReleasedValue = Call.getArgSVal(0).getAsSymbol(); 3810b57cec5SDimitry Andric if (!ReleasedValue) 3820b57cec5SDimitry Andric return; 3830b57cec5SDimitry Andric 3840b57cec5SDimitry Andric transitionToReleaseValue(C, ReleasedValue); 3850b57cec5SDimitry Andric } 3860b57cec5SDimitry Andric /// If the message was a call to '[super dealloc]', diagnose any missing 3870b57cec5SDimitry Andric /// releases. 3880b57cec5SDimitry Andric void ObjCDeallocChecker::checkPostObjCMessage( 3890b57cec5SDimitry Andric const ObjCMethodCall &M, CheckerContext &C) const { 3900b57cec5SDimitry Andric // We perform this check post-message so that if the super -dealloc 3910b57cec5SDimitry Andric // calls a helper method and that this class overrides, any ivars released in 3920b57cec5SDimitry Andric // the helper method will be recorded before checking. 3930b57cec5SDimitry Andric if (isSuperDeallocMessage(M)) 3940b57cec5SDimitry Andric diagnoseMissingReleases(C); 3950b57cec5SDimitry Andric } 3960b57cec5SDimitry Andric 3970b57cec5SDimitry Andric /// Check for missing releases even when -dealloc does not call 3980b57cec5SDimitry Andric /// '[super dealloc]'. 3990b57cec5SDimitry Andric void ObjCDeallocChecker::checkEndFunction( 4000b57cec5SDimitry Andric const ReturnStmt *RS, CheckerContext &C) const { 4010b57cec5SDimitry Andric diagnoseMissingReleases(C); 4020b57cec5SDimitry Andric } 4030b57cec5SDimitry Andric 4040b57cec5SDimitry Andric /// Check for missing releases on early return. 4050b57cec5SDimitry Andric void ObjCDeallocChecker::checkPreStmt( 4060b57cec5SDimitry Andric const ReturnStmt *RS, CheckerContext &C) const { 4070b57cec5SDimitry Andric diagnoseMissingReleases(C); 4080b57cec5SDimitry Andric } 4090b57cec5SDimitry Andric 4100b57cec5SDimitry Andric /// When a symbol is assumed to be nil, remove it from the set of symbols 4110b57cec5SDimitry Andric /// require to be nil. 4120b57cec5SDimitry Andric ProgramStateRef ObjCDeallocChecker::evalAssume(ProgramStateRef State, SVal Cond, 4130b57cec5SDimitry Andric bool Assumption) const { 4140b57cec5SDimitry Andric if (State->get<UnreleasedIvarMap>().isEmpty()) 4150b57cec5SDimitry Andric return State; 4160b57cec5SDimitry Andric 417e8d8bef9SDimitry Andric auto *CondBSE = dyn_cast_or_null<BinarySymExpr>(Cond.getAsSymbol()); 4180b57cec5SDimitry Andric if (!CondBSE) 4190b57cec5SDimitry Andric return State; 4200b57cec5SDimitry Andric 4210b57cec5SDimitry Andric BinaryOperator::Opcode OpCode = CondBSE->getOpcode(); 4220b57cec5SDimitry Andric if (Assumption) { 4230b57cec5SDimitry Andric if (OpCode != BO_EQ) 4240b57cec5SDimitry Andric return State; 4250b57cec5SDimitry Andric } else { 4260b57cec5SDimitry Andric if (OpCode != BO_NE) 4270b57cec5SDimitry Andric return State; 4280b57cec5SDimitry Andric } 4290b57cec5SDimitry Andric 4300b57cec5SDimitry Andric SymbolRef NullSymbol = nullptr; 4310b57cec5SDimitry Andric if (auto *SIE = dyn_cast<SymIntExpr>(CondBSE)) { 4320b57cec5SDimitry Andric const llvm::APInt &RHS = SIE->getRHS(); 4330b57cec5SDimitry Andric if (RHS != 0) 4340b57cec5SDimitry Andric return State; 4350b57cec5SDimitry Andric NullSymbol = SIE->getLHS(); 4360b57cec5SDimitry Andric } else if (auto *SIE = dyn_cast<IntSymExpr>(CondBSE)) { 4370b57cec5SDimitry Andric const llvm::APInt &LHS = SIE->getLHS(); 4380b57cec5SDimitry Andric if (LHS != 0) 4390b57cec5SDimitry Andric return State; 4400b57cec5SDimitry Andric NullSymbol = SIE->getRHS(); 4410b57cec5SDimitry Andric } else { 4420b57cec5SDimitry Andric return State; 4430b57cec5SDimitry Andric } 4440b57cec5SDimitry Andric 4450b57cec5SDimitry Andric SymbolRef InstanceSymbol = getInstanceSymbolFromIvarSymbol(NullSymbol); 4460b57cec5SDimitry Andric if (!InstanceSymbol) 4470b57cec5SDimitry Andric return State; 4480b57cec5SDimitry Andric 4490b57cec5SDimitry Andric State = removeValueRequiringRelease(State, InstanceSymbol, NullSymbol); 4500b57cec5SDimitry Andric 4510b57cec5SDimitry Andric return State; 4520b57cec5SDimitry Andric } 4530b57cec5SDimitry Andric 4540b57cec5SDimitry Andric /// If a symbol escapes conservatively assume unseen code released it. 4550b57cec5SDimitry Andric ProgramStateRef ObjCDeallocChecker::checkPointerEscape( 4560b57cec5SDimitry Andric ProgramStateRef State, const InvalidatedSymbols &Escaped, 4570b57cec5SDimitry Andric const CallEvent *Call, PointerEscapeKind Kind) const { 4580b57cec5SDimitry Andric 4590b57cec5SDimitry Andric if (State->get<UnreleasedIvarMap>().isEmpty()) 4600b57cec5SDimitry Andric return State; 4610b57cec5SDimitry Andric 4620b57cec5SDimitry Andric // Don't treat calls to '[super dealloc]' as escaping for the purposes 4630b57cec5SDimitry Andric // of this checker. Because the checker diagnoses missing releases in the 4640b57cec5SDimitry Andric // post-message handler for '[super dealloc], escaping here would cause 4650b57cec5SDimitry Andric // the checker to never warn. 4660b57cec5SDimitry Andric auto *OMC = dyn_cast_or_null<ObjCMethodCall>(Call); 4670b57cec5SDimitry Andric if (OMC && isSuperDeallocMessage(*OMC)) 4680b57cec5SDimitry Andric return State; 4690b57cec5SDimitry Andric 4700b57cec5SDimitry Andric for (const auto &Sym : Escaped) { 4710b57cec5SDimitry Andric if (!Call || (Call && !Call->isInSystemHeader())) { 4720b57cec5SDimitry Andric // If Sym is a symbol for an object with instance variables that 4730b57cec5SDimitry Andric // must be released, remove these obligations when the object escapes 4740b57cec5SDimitry Andric // unless via a call to a system function. System functions are 4750b57cec5SDimitry Andric // very unlikely to release instance variables on objects passed to them, 4760b57cec5SDimitry Andric // and are frequently called on 'self' in -dealloc (e.g., to remove 4770b57cec5SDimitry Andric // observers) -- we want to avoid false negatives from escaping on 4780b57cec5SDimitry Andric // them. 4790b57cec5SDimitry Andric State = State->remove<UnreleasedIvarMap>(Sym); 4800b57cec5SDimitry Andric } 4810b57cec5SDimitry Andric 4820b57cec5SDimitry Andric 4830b57cec5SDimitry Andric SymbolRef InstanceSymbol = getInstanceSymbolFromIvarSymbol(Sym); 4840b57cec5SDimitry Andric if (!InstanceSymbol) 4850b57cec5SDimitry Andric continue; 4860b57cec5SDimitry Andric 4870b57cec5SDimitry Andric State = removeValueRequiringRelease(State, InstanceSymbol, Sym); 4880b57cec5SDimitry Andric } 4890b57cec5SDimitry Andric 4900b57cec5SDimitry Andric return State; 4910b57cec5SDimitry Andric } 4920b57cec5SDimitry Andric 4930b57cec5SDimitry Andric /// Report any unreleased instance variables for the current instance being 4940b57cec5SDimitry Andric /// dealloced. 4950b57cec5SDimitry Andric void ObjCDeallocChecker::diagnoseMissingReleases(CheckerContext &C) const { 4960b57cec5SDimitry Andric ProgramStateRef State = C.getState(); 4970b57cec5SDimitry Andric 4980b57cec5SDimitry Andric SVal SelfVal; 4990b57cec5SDimitry Andric if (!isInInstanceDealloc(C, SelfVal)) 5000b57cec5SDimitry Andric return; 5010b57cec5SDimitry Andric 5020b57cec5SDimitry Andric const MemRegion *SelfRegion = SelfVal.castAs<loc::MemRegionVal>().getRegion(); 5030b57cec5SDimitry Andric const LocationContext *LCtx = C.getLocationContext(); 5040b57cec5SDimitry Andric 5050b57cec5SDimitry Andric ExplodedNode *ErrNode = nullptr; 5060b57cec5SDimitry Andric 5070b57cec5SDimitry Andric SymbolRef SelfSym = SelfVal.getAsSymbol(); 5080b57cec5SDimitry Andric if (!SelfSym) 5090b57cec5SDimitry Andric return; 5100b57cec5SDimitry Andric 5110b57cec5SDimitry Andric const SymbolSet *OldUnreleased = State->get<UnreleasedIvarMap>(SelfSym); 5120b57cec5SDimitry Andric if (!OldUnreleased) 5130b57cec5SDimitry Andric return; 5140b57cec5SDimitry Andric 5150b57cec5SDimitry Andric SymbolSet NewUnreleased = *OldUnreleased; 5160b57cec5SDimitry Andric SymbolSet::Factory &F = State->getStateManager().get_context<SymbolSet>(); 5170b57cec5SDimitry Andric 5180b57cec5SDimitry Andric ProgramStateRef InitialState = State; 5190b57cec5SDimitry Andric 5200b57cec5SDimitry Andric for (auto *IvarSymbol : *OldUnreleased) { 5210b57cec5SDimitry Andric const TypedValueRegion *TVR = 5220b57cec5SDimitry Andric cast<SymbolRegionValue>(IvarSymbol)->getRegion(); 5230b57cec5SDimitry Andric const ObjCIvarRegion *IvarRegion = cast<ObjCIvarRegion>(TVR); 5240b57cec5SDimitry Andric 5250b57cec5SDimitry Andric // Don't warn if the ivar is not for this instance. 5260b57cec5SDimitry Andric if (SelfRegion != IvarRegion->getSuperRegion()) 5270b57cec5SDimitry Andric continue; 5280b57cec5SDimitry Andric 5290b57cec5SDimitry Andric const ObjCIvarDecl *IvarDecl = IvarRegion->getDecl(); 5300b57cec5SDimitry Andric // Prevent an inlined call to -dealloc in a super class from warning 5310b57cec5SDimitry Andric // about the values the subclass's -dealloc should release. 5320b57cec5SDimitry Andric if (IvarDecl->getContainingInterface() != 5330b57cec5SDimitry Andric cast<ObjCMethodDecl>(LCtx->getDecl())->getClassInterface()) 5340b57cec5SDimitry Andric continue; 5350b57cec5SDimitry Andric 5360b57cec5SDimitry Andric // Prevents diagnosing multiple times for the same instance variable 5370b57cec5SDimitry Andric // at, for example, both a return and at the end of the function. 5380b57cec5SDimitry Andric NewUnreleased = F.remove(NewUnreleased, IvarSymbol); 5390b57cec5SDimitry Andric 5400b57cec5SDimitry Andric if (State->getStateManager() 5410b57cec5SDimitry Andric .getConstraintManager() 5420b57cec5SDimitry Andric .isNull(State, IvarSymbol) 5430b57cec5SDimitry Andric .isConstrainedTrue()) { 5440b57cec5SDimitry Andric continue; 5450b57cec5SDimitry Andric } 5460b57cec5SDimitry Andric 5470b57cec5SDimitry Andric // A missing release manifests as a leak, so treat as a non-fatal error. 5480b57cec5SDimitry Andric if (!ErrNode) 5490b57cec5SDimitry Andric ErrNode = C.generateNonFatalErrorNode(); 5500b57cec5SDimitry Andric // If we've already reached this node on another path, return without 5510b57cec5SDimitry Andric // diagnosing. 5520b57cec5SDimitry Andric if (!ErrNode) 5530b57cec5SDimitry Andric return; 5540b57cec5SDimitry Andric 5550b57cec5SDimitry Andric std::string Buf; 5560b57cec5SDimitry Andric llvm::raw_string_ostream OS(Buf); 5570b57cec5SDimitry Andric 5580b57cec5SDimitry Andric const ObjCInterfaceDecl *Interface = IvarDecl->getContainingInterface(); 5590b57cec5SDimitry Andric // If the class is known to have a lifecycle with teardown that is 5600b57cec5SDimitry Andric // separate from -dealloc, do not warn about missing releases. We 5610b57cec5SDimitry Andric // suppress here (rather than not tracking for instance variables in 5620b57cec5SDimitry Andric // such classes) because these classes are rare. 5630b57cec5SDimitry Andric if (classHasSeparateTeardown(Interface)) 5640b57cec5SDimitry Andric return; 5650b57cec5SDimitry Andric 5660b57cec5SDimitry Andric ObjCImplDecl *ImplDecl = Interface->getImplementation(); 5670b57cec5SDimitry Andric 5680b57cec5SDimitry Andric const ObjCPropertyImplDecl *PropImpl = 5690b57cec5SDimitry Andric ImplDecl->FindPropertyImplIvarDecl(IvarDecl->getIdentifier()); 5700b57cec5SDimitry Andric 5710b57cec5SDimitry Andric const ObjCPropertyDecl *PropDecl = PropImpl->getPropertyDecl(); 5720b57cec5SDimitry Andric 5730b57cec5SDimitry Andric assert(PropDecl->getSetterKind() == ObjCPropertyDecl::Copy || 5740b57cec5SDimitry Andric PropDecl->getSetterKind() == ObjCPropertyDecl::Retain); 5750b57cec5SDimitry Andric 5760b57cec5SDimitry Andric OS << "The '" << *IvarDecl << "' ivar in '" << *ImplDecl 5770b57cec5SDimitry Andric << "' was "; 5780b57cec5SDimitry Andric 5790b57cec5SDimitry Andric if (PropDecl->getSetterKind() == ObjCPropertyDecl::Retain) 5800b57cec5SDimitry Andric OS << "retained"; 5810b57cec5SDimitry Andric else 5820b57cec5SDimitry Andric OS << "copied"; 5830b57cec5SDimitry Andric 5840b57cec5SDimitry Andric OS << " by a synthesized property but not released" 5850b57cec5SDimitry Andric " before '[super dealloc]'"; 5860b57cec5SDimitry Andric 587647cbc5dSDimitry Andric auto BR = std::make_unique<PathSensitiveBugReport>(MissingReleaseBugType, 588*0fca6ea1SDimitry Andric Buf, ErrNode); 5890b57cec5SDimitry Andric C.emitReport(std::move(BR)); 5900b57cec5SDimitry Andric } 5910b57cec5SDimitry Andric 5920b57cec5SDimitry Andric if (NewUnreleased.isEmpty()) { 5930b57cec5SDimitry Andric State = State->remove<UnreleasedIvarMap>(SelfSym); 5940b57cec5SDimitry Andric } else { 5950b57cec5SDimitry Andric State = State->set<UnreleasedIvarMap>(SelfSym, NewUnreleased); 5960b57cec5SDimitry Andric } 5970b57cec5SDimitry Andric 5980b57cec5SDimitry Andric if (ErrNode) { 5990b57cec5SDimitry Andric C.addTransition(State, ErrNode); 6000b57cec5SDimitry Andric } else if (State != InitialState) { 6010b57cec5SDimitry Andric C.addTransition(State); 6020b57cec5SDimitry Andric } 6030b57cec5SDimitry Andric 6040b57cec5SDimitry Andric // Make sure that after checking in the top-most frame the list of 6050b57cec5SDimitry Andric // tracked ivars is empty. This is intended to detect accidental leaks in 6060b57cec5SDimitry Andric // the UnreleasedIvarMap program state. 6070b57cec5SDimitry Andric assert(!LCtx->inTopFrame() || State->get<UnreleasedIvarMap>().isEmpty()); 6080b57cec5SDimitry Andric } 6090b57cec5SDimitry Andric 6100b57cec5SDimitry Andric /// Given a symbol, determine whether the symbol refers to an ivar on 6110b57cec5SDimitry Andric /// the top-most deallocating instance. If so, find the property for that 6120b57cec5SDimitry Andric /// ivar, if one exists. Otherwise return null. 6130b57cec5SDimitry Andric const ObjCPropertyImplDecl * 6140b57cec5SDimitry Andric ObjCDeallocChecker::findPropertyOnDeallocatingInstance( 6150b57cec5SDimitry Andric SymbolRef IvarSym, CheckerContext &C) const { 6160b57cec5SDimitry Andric SVal DeallocedInstance; 6170b57cec5SDimitry Andric if (!isInInstanceDealloc(C, DeallocedInstance)) 6180b57cec5SDimitry Andric return nullptr; 6190b57cec5SDimitry Andric 6200b57cec5SDimitry Andric // Try to get the region from which the ivar value was loaded. 6210b57cec5SDimitry Andric auto *IvarRegion = getIvarRegionForIvarSymbol(IvarSym); 6220b57cec5SDimitry Andric if (!IvarRegion) 6230b57cec5SDimitry Andric return nullptr; 6240b57cec5SDimitry Andric 6250b57cec5SDimitry Andric // Don't try to find the property if the ivar was not loaded from the 6260b57cec5SDimitry Andric // given instance. 6270b57cec5SDimitry Andric if (DeallocedInstance.castAs<loc::MemRegionVal>().getRegion() != 6280b57cec5SDimitry Andric IvarRegion->getSuperRegion()) 6290b57cec5SDimitry Andric return nullptr; 6300b57cec5SDimitry Andric 6310b57cec5SDimitry Andric const LocationContext *LCtx = C.getLocationContext(); 6320b57cec5SDimitry Andric const ObjCIvarDecl *IvarDecl = IvarRegion->getDecl(); 6330b57cec5SDimitry Andric 6340b57cec5SDimitry Andric const ObjCImplDecl *Container = getContainingObjCImpl(LCtx); 6350b57cec5SDimitry Andric const ObjCPropertyImplDecl *PropImpl = 6360b57cec5SDimitry Andric Container->FindPropertyImplIvarDecl(IvarDecl->getIdentifier()); 6370b57cec5SDimitry Andric return PropImpl; 6380b57cec5SDimitry Andric } 6390b57cec5SDimitry Andric 6400b57cec5SDimitry Andric /// Emits a warning if the current context is -dealloc and ReleasedValue 6410b57cec5SDimitry Andric /// must not be directly released in a -dealloc. Returns true if a diagnostic 6420b57cec5SDimitry Andric /// was emitted. 6430b57cec5SDimitry Andric bool ObjCDeallocChecker::diagnoseExtraRelease(SymbolRef ReleasedValue, 6440b57cec5SDimitry Andric const ObjCMethodCall &M, 6450b57cec5SDimitry Andric CheckerContext &C) const { 6460b57cec5SDimitry Andric // Try to get the region from which the released value was loaded. 6470b57cec5SDimitry Andric // Note that, unlike diagnosing for missing releases, here we don't track 6480b57cec5SDimitry Andric // values that must not be released in the state. This is because even if 6490b57cec5SDimitry Andric // these values escape, it is still an error under the rules of MRR to 6500b57cec5SDimitry Andric // release them in -dealloc. 6510b57cec5SDimitry Andric const ObjCPropertyImplDecl *PropImpl = 6520b57cec5SDimitry Andric findPropertyOnDeallocatingInstance(ReleasedValue, C); 6530b57cec5SDimitry Andric 6540b57cec5SDimitry Andric if (!PropImpl) 6550b57cec5SDimitry Andric return false; 6560b57cec5SDimitry Andric 6570b57cec5SDimitry Andric // If the ivar belongs to a property that must not be released directly 6580b57cec5SDimitry Andric // in dealloc, emit a warning. 6590b57cec5SDimitry Andric if (getDeallocReleaseRequirement(PropImpl) != 6600b57cec5SDimitry Andric ReleaseRequirement::MustNotReleaseDirectly) { 6610b57cec5SDimitry Andric return false; 6620b57cec5SDimitry Andric } 6630b57cec5SDimitry Andric 6640b57cec5SDimitry Andric // If the property is readwrite but it shadows a read-only property in its 6650b57cec5SDimitry Andric // external interface, treat the property a read-only. If the outside 6660b57cec5SDimitry Andric // world cannot write to a property then the internal implementation is free 6670b57cec5SDimitry Andric // to make its own convention about whether the value is stored retained 6680b57cec5SDimitry Andric // or not. We look up the shadow here rather than in 6690b57cec5SDimitry Andric // getDeallocReleaseRequirement() because doing so can be expensive. 6700b57cec5SDimitry Andric const ObjCPropertyDecl *PropDecl = findShadowedPropertyDecl(PropImpl); 6710b57cec5SDimitry Andric if (PropDecl) { 6720b57cec5SDimitry Andric if (PropDecl->isReadOnly()) 6730b57cec5SDimitry Andric return false; 6740b57cec5SDimitry Andric } else { 6750b57cec5SDimitry Andric PropDecl = PropImpl->getPropertyDecl(); 6760b57cec5SDimitry Andric } 6770b57cec5SDimitry Andric 6780b57cec5SDimitry Andric ExplodedNode *ErrNode = C.generateNonFatalErrorNode(); 6790b57cec5SDimitry Andric if (!ErrNode) 6800b57cec5SDimitry Andric return false; 6810b57cec5SDimitry Andric 6820b57cec5SDimitry Andric std::string Buf; 6830b57cec5SDimitry Andric llvm::raw_string_ostream OS(Buf); 6840b57cec5SDimitry Andric 6850b57cec5SDimitry Andric assert(PropDecl->getSetterKind() == ObjCPropertyDecl::Weak || 6860b57cec5SDimitry Andric (PropDecl->getSetterKind() == ObjCPropertyDecl::Assign && 6870b57cec5SDimitry Andric !PropDecl->isReadOnly()) || 6880b57cec5SDimitry Andric isReleasedByCIFilterDealloc(PropImpl) 6890b57cec5SDimitry Andric ); 6900b57cec5SDimitry Andric 6910b57cec5SDimitry Andric const ObjCImplDecl *Container = getContainingObjCImpl(C.getLocationContext()); 6920b57cec5SDimitry Andric OS << "The '" << *PropImpl->getPropertyIvarDecl() 6930b57cec5SDimitry Andric << "' ivar in '" << *Container; 6940b57cec5SDimitry Andric 6950b57cec5SDimitry Andric 6960b57cec5SDimitry Andric if (isReleasedByCIFilterDealloc(PropImpl)) { 6970b57cec5SDimitry Andric OS << "' will be released by '-[CIFilter dealloc]' but also released here"; 6980b57cec5SDimitry Andric } else { 6990b57cec5SDimitry Andric OS << "' was synthesized for "; 7000b57cec5SDimitry Andric 7010b57cec5SDimitry Andric if (PropDecl->getSetterKind() == ObjCPropertyDecl::Weak) 7020b57cec5SDimitry Andric OS << "a weak"; 7030b57cec5SDimitry Andric else 7040b57cec5SDimitry Andric OS << "an assign, readwrite"; 7050b57cec5SDimitry Andric 7060b57cec5SDimitry Andric OS << " property but was released in 'dealloc'"; 7070b57cec5SDimitry Andric } 7080b57cec5SDimitry Andric 709*0fca6ea1SDimitry Andric auto BR = std::make_unique<PathSensitiveBugReport>(ExtraReleaseBugType, Buf, 710*0fca6ea1SDimitry Andric ErrNode); 7110b57cec5SDimitry Andric BR->addRange(M.getOriginExpr()->getSourceRange()); 7120b57cec5SDimitry Andric 7130b57cec5SDimitry Andric C.emitReport(std::move(BR)); 7140b57cec5SDimitry Andric 7150b57cec5SDimitry Andric return true; 7160b57cec5SDimitry Andric } 7170b57cec5SDimitry Andric 7180b57cec5SDimitry Andric /// Emits a warning if the current context is -dealloc and DeallocedValue 7190b57cec5SDimitry Andric /// must not be directly dealloced in a -dealloc. Returns true if a diagnostic 7200b57cec5SDimitry Andric /// was emitted. 7210b57cec5SDimitry Andric bool ObjCDeallocChecker::diagnoseMistakenDealloc(SymbolRef DeallocedValue, 7220b57cec5SDimitry Andric const ObjCMethodCall &M, 7230b57cec5SDimitry Andric CheckerContext &C) const { 7240b57cec5SDimitry Andric // TODO: Apart from unknown/undefined receivers, this may happen when 7250b57cec5SDimitry Andric // dealloc is called as a class method. Should we warn? 7260b57cec5SDimitry Andric if (!DeallocedValue) 7270b57cec5SDimitry Andric return false; 7280b57cec5SDimitry Andric 7290b57cec5SDimitry Andric // Find the property backing the instance variable that M 7300b57cec5SDimitry Andric // is dealloc'ing. 7310b57cec5SDimitry Andric const ObjCPropertyImplDecl *PropImpl = 7320b57cec5SDimitry Andric findPropertyOnDeallocatingInstance(DeallocedValue, C); 7330b57cec5SDimitry Andric if (!PropImpl) 7340b57cec5SDimitry Andric return false; 7350b57cec5SDimitry Andric 7360b57cec5SDimitry Andric if (getDeallocReleaseRequirement(PropImpl) != 7370b57cec5SDimitry Andric ReleaseRequirement::MustRelease) { 7380b57cec5SDimitry Andric return false; 7390b57cec5SDimitry Andric } 7400b57cec5SDimitry Andric 7410b57cec5SDimitry Andric ExplodedNode *ErrNode = C.generateErrorNode(); 7420b57cec5SDimitry Andric if (!ErrNode) 7430b57cec5SDimitry Andric return false; 7440b57cec5SDimitry Andric 7450b57cec5SDimitry Andric std::string Buf; 7460b57cec5SDimitry Andric llvm::raw_string_ostream OS(Buf); 7470b57cec5SDimitry Andric 7480b57cec5SDimitry Andric OS << "'" << *PropImpl->getPropertyIvarDecl() 7490b57cec5SDimitry Andric << "' should be released rather than deallocated"; 7500b57cec5SDimitry Andric 751647cbc5dSDimitry Andric auto BR = std::make_unique<PathSensitiveBugReport>(MistakenDeallocBugType, 752*0fca6ea1SDimitry Andric Buf, ErrNode); 7530b57cec5SDimitry Andric BR->addRange(M.getOriginExpr()->getSourceRange()); 7540b57cec5SDimitry Andric 7550b57cec5SDimitry Andric C.emitReport(std::move(BR)); 7560b57cec5SDimitry Andric 7570b57cec5SDimitry Andric return true; 7580b57cec5SDimitry Andric } 7590b57cec5SDimitry Andric 7600b57cec5SDimitry Andric void ObjCDeallocChecker::initIdentifierInfoAndSelectors( 7610b57cec5SDimitry Andric ASTContext &Ctx) const { 7620b57cec5SDimitry Andric if (NSObjectII) 7630b57cec5SDimitry Andric return; 7640b57cec5SDimitry Andric 7650b57cec5SDimitry Andric NSObjectII = &Ctx.Idents.get("NSObject"); 7660b57cec5SDimitry Andric SenTestCaseII = &Ctx.Idents.get("SenTestCase"); 7670b57cec5SDimitry Andric XCTestCaseII = &Ctx.Idents.get("XCTestCase"); 7680b57cec5SDimitry Andric Block_releaseII = &Ctx.Idents.get("_Block_release"); 7690b57cec5SDimitry Andric CIFilterII = &Ctx.Idents.get("CIFilter"); 7700b57cec5SDimitry Andric 771*0fca6ea1SDimitry Andric const IdentifierInfo *DeallocII = &Ctx.Idents.get("dealloc"); 772*0fca6ea1SDimitry Andric const IdentifierInfo *ReleaseII = &Ctx.Idents.get("release"); 7730b57cec5SDimitry Andric DeallocSel = Ctx.Selectors.getSelector(0, &DeallocII); 7740b57cec5SDimitry Andric ReleaseSel = Ctx.Selectors.getSelector(0, &ReleaseII); 7750b57cec5SDimitry Andric } 7760b57cec5SDimitry Andric 7770b57cec5SDimitry Andric /// Returns true if M is a call to '[super dealloc]'. 7780b57cec5SDimitry Andric bool ObjCDeallocChecker::isSuperDeallocMessage( 7790b57cec5SDimitry Andric const ObjCMethodCall &M) const { 7800b57cec5SDimitry Andric if (M.getOriginExpr()->getReceiverKind() != ObjCMessageExpr::SuperInstance) 7810b57cec5SDimitry Andric return false; 7820b57cec5SDimitry Andric 7830b57cec5SDimitry Andric return M.getSelector() == DeallocSel; 7840b57cec5SDimitry Andric } 7850b57cec5SDimitry Andric 7860b57cec5SDimitry Andric /// Returns the ObjCImplDecl containing the method declaration in LCtx. 7870b57cec5SDimitry Andric const ObjCImplDecl * 7880b57cec5SDimitry Andric ObjCDeallocChecker::getContainingObjCImpl(const LocationContext *LCtx) const { 7890b57cec5SDimitry Andric auto *MD = cast<ObjCMethodDecl>(LCtx->getDecl()); 7900b57cec5SDimitry Andric return cast<ObjCImplDecl>(MD->getDeclContext()); 7910b57cec5SDimitry Andric } 7920b57cec5SDimitry Andric 7930b57cec5SDimitry Andric /// Returns the property that shadowed by PropImpl if one exists and 7940b57cec5SDimitry Andric /// nullptr otherwise. 7950b57cec5SDimitry Andric const ObjCPropertyDecl *ObjCDeallocChecker::findShadowedPropertyDecl( 7960b57cec5SDimitry Andric const ObjCPropertyImplDecl *PropImpl) const { 7970b57cec5SDimitry Andric const ObjCPropertyDecl *PropDecl = PropImpl->getPropertyDecl(); 7980b57cec5SDimitry Andric 7990b57cec5SDimitry Andric // Only readwrite properties can shadow. 8000b57cec5SDimitry Andric if (PropDecl->isReadOnly()) 8010b57cec5SDimitry Andric return nullptr; 8020b57cec5SDimitry Andric 8030b57cec5SDimitry Andric auto *CatDecl = dyn_cast<ObjCCategoryDecl>(PropDecl->getDeclContext()); 8040b57cec5SDimitry Andric 8050b57cec5SDimitry Andric // Only class extensions can contain shadowing properties. 8060b57cec5SDimitry Andric if (!CatDecl || !CatDecl->IsClassExtension()) 8070b57cec5SDimitry Andric return nullptr; 8080b57cec5SDimitry Andric 8090b57cec5SDimitry Andric IdentifierInfo *ID = PropDecl->getIdentifier(); 8100b57cec5SDimitry Andric DeclContext::lookup_result R = CatDecl->getClassInterface()->lookup(ID); 81106c3fb27SDimitry Andric for (const NamedDecl *D : R) { 81206c3fb27SDimitry Andric auto *ShadowedPropDecl = dyn_cast<ObjCPropertyDecl>(D); 8130b57cec5SDimitry Andric if (!ShadowedPropDecl) 8140b57cec5SDimitry Andric continue; 8150b57cec5SDimitry Andric 8160b57cec5SDimitry Andric if (ShadowedPropDecl->isInstanceProperty()) { 8170b57cec5SDimitry Andric assert(ShadowedPropDecl->isReadOnly()); 8180b57cec5SDimitry Andric return ShadowedPropDecl; 8190b57cec5SDimitry Andric } 8200b57cec5SDimitry Andric } 8210b57cec5SDimitry Andric 8220b57cec5SDimitry Andric return nullptr; 8230b57cec5SDimitry Andric } 8240b57cec5SDimitry Andric 8250b57cec5SDimitry Andric /// Add a transition noting the release of the given value. 8260b57cec5SDimitry Andric void ObjCDeallocChecker::transitionToReleaseValue(CheckerContext &C, 8270b57cec5SDimitry Andric SymbolRef Value) const { 8280b57cec5SDimitry Andric assert(Value); 8290b57cec5SDimitry Andric SymbolRef InstanceSym = getInstanceSymbolFromIvarSymbol(Value); 8300b57cec5SDimitry Andric if (!InstanceSym) 8310b57cec5SDimitry Andric return; 8320b57cec5SDimitry Andric ProgramStateRef InitialState = C.getState(); 8330b57cec5SDimitry Andric 8340b57cec5SDimitry Andric ProgramStateRef ReleasedState = 8350b57cec5SDimitry Andric removeValueRequiringRelease(InitialState, InstanceSym, Value); 8360b57cec5SDimitry Andric 8370b57cec5SDimitry Andric if (ReleasedState != InitialState) { 8380b57cec5SDimitry Andric C.addTransition(ReleasedState); 8390b57cec5SDimitry Andric } 8400b57cec5SDimitry Andric } 8410b57cec5SDimitry Andric 8420b57cec5SDimitry Andric /// Remove the Value requiring a release from the tracked set for 8430b57cec5SDimitry Andric /// Instance and return the resultant state. 8440b57cec5SDimitry Andric ProgramStateRef ObjCDeallocChecker::removeValueRequiringRelease( 8450b57cec5SDimitry Andric ProgramStateRef State, SymbolRef Instance, SymbolRef Value) const { 8460b57cec5SDimitry Andric assert(Instance); 8470b57cec5SDimitry Andric assert(Value); 8480b57cec5SDimitry Andric const ObjCIvarRegion *RemovedRegion = getIvarRegionForIvarSymbol(Value); 8490b57cec5SDimitry Andric if (!RemovedRegion) 8500b57cec5SDimitry Andric return State; 8510b57cec5SDimitry Andric 8520b57cec5SDimitry Andric const SymbolSet *Unreleased = State->get<UnreleasedIvarMap>(Instance); 8530b57cec5SDimitry Andric if (!Unreleased) 8540b57cec5SDimitry Andric return State; 8550b57cec5SDimitry Andric 8560b57cec5SDimitry Andric // Mark the value as no longer requiring a release. 8570b57cec5SDimitry Andric SymbolSet::Factory &F = State->getStateManager().get_context<SymbolSet>(); 8580b57cec5SDimitry Andric SymbolSet NewUnreleased = *Unreleased; 8590b57cec5SDimitry Andric for (auto &Sym : *Unreleased) { 8600b57cec5SDimitry Andric const ObjCIvarRegion *UnreleasedRegion = getIvarRegionForIvarSymbol(Sym); 8610b57cec5SDimitry Andric assert(UnreleasedRegion); 8620b57cec5SDimitry Andric if (RemovedRegion->getDecl() == UnreleasedRegion->getDecl()) { 8630b57cec5SDimitry Andric NewUnreleased = F.remove(NewUnreleased, Sym); 8640b57cec5SDimitry Andric } 8650b57cec5SDimitry Andric } 8660b57cec5SDimitry Andric 8670b57cec5SDimitry Andric if (NewUnreleased.isEmpty()) { 8680b57cec5SDimitry Andric return State->remove<UnreleasedIvarMap>(Instance); 8690b57cec5SDimitry Andric } 8700b57cec5SDimitry Andric 8710b57cec5SDimitry Andric return State->set<UnreleasedIvarMap>(Instance, NewUnreleased); 8720b57cec5SDimitry Andric } 8730b57cec5SDimitry Andric 8740b57cec5SDimitry Andric /// Determines whether the instance variable for \p PropImpl must or must not be 8750b57cec5SDimitry Andric /// released in -dealloc or whether it cannot be determined. 8760b57cec5SDimitry Andric ReleaseRequirement ObjCDeallocChecker::getDeallocReleaseRequirement( 8770b57cec5SDimitry Andric const ObjCPropertyImplDecl *PropImpl) const { 8780b57cec5SDimitry Andric const ObjCIvarDecl *IvarDecl; 8790b57cec5SDimitry Andric const ObjCPropertyDecl *PropDecl; 8800b57cec5SDimitry Andric if (!isSynthesizedRetainableProperty(PropImpl, &IvarDecl, &PropDecl)) 8810b57cec5SDimitry Andric return ReleaseRequirement::Unknown; 8820b57cec5SDimitry Andric 8830b57cec5SDimitry Andric ObjCPropertyDecl::SetterKind SK = PropDecl->getSetterKind(); 8840b57cec5SDimitry Andric 8850b57cec5SDimitry Andric switch (SK) { 8860b57cec5SDimitry Andric // Retain and copy setters retain/copy their values before storing and so 8870b57cec5SDimitry Andric // the value in their instance variables must be released in -dealloc. 8880b57cec5SDimitry Andric case ObjCPropertyDecl::Retain: 8890b57cec5SDimitry Andric case ObjCPropertyDecl::Copy: 8900b57cec5SDimitry Andric if (isReleasedByCIFilterDealloc(PropImpl)) 8910b57cec5SDimitry Andric return ReleaseRequirement::MustNotReleaseDirectly; 8920b57cec5SDimitry Andric 8930b57cec5SDimitry Andric if (isNibLoadedIvarWithoutRetain(PropImpl)) 8940b57cec5SDimitry Andric return ReleaseRequirement::Unknown; 8950b57cec5SDimitry Andric 8960b57cec5SDimitry Andric return ReleaseRequirement::MustRelease; 8970b57cec5SDimitry Andric 8980b57cec5SDimitry Andric case ObjCPropertyDecl::Weak: 8990b57cec5SDimitry Andric return ReleaseRequirement::MustNotReleaseDirectly; 9000b57cec5SDimitry Andric 9010b57cec5SDimitry Andric case ObjCPropertyDecl::Assign: 9020b57cec5SDimitry Andric // It is common for the ivars for read-only assign properties to 9030b57cec5SDimitry Andric // always be stored retained, so their release requirement cannot be 9040b57cec5SDimitry Andric // be determined. 9050b57cec5SDimitry Andric if (PropDecl->isReadOnly()) 9060b57cec5SDimitry Andric return ReleaseRequirement::Unknown; 9070b57cec5SDimitry Andric 9080b57cec5SDimitry Andric return ReleaseRequirement::MustNotReleaseDirectly; 9090b57cec5SDimitry Andric } 9100b57cec5SDimitry Andric llvm_unreachable("Unrecognized setter kind"); 9110b57cec5SDimitry Andric } 9120b57cec5SDimitry Andric 9130b57cec5SDimitry Andric /// Returns the released value if M is a call a setter that releases 9140b57cec5SDimitry Andric /// and nils out its underlying instance variable. 9150b57cec5SDimitry Andric SymbolRef 9160b57cec5SDimitry Andric ObjCDeallocChecker::getValueReleasedByNillingOut(const ObjCMethodCall &M, 9170b57cec5SDimitry Andric CheckerContext &C) const { 9180b57cec5SDimitry Andric SVal ReceiverVal = M.getReceiverSVal(); 9190b57cec5SDimitry Andric if (!ReceiverVal.isValid()) 9200b57cec5SDimitry Andric return nullptr; 9210b57cec5SDimitry Andric 9220b57cec5SDimitry Andric if (M.getNumArgs() == 0) 9230b57cec5SDimitry Andric return nullptr; 9240b57cec5SDimitry Andric 9250b57cec5SDimitry Andric if (!M.getArgExpr(0)->getType()->isObjCRetainableType()) 9260b57cec5SDimitry Andric return nullptr; 9270b57cec5SDimitry Andric 9280b57cec5SDimitry Andric // Is the first argument nil? 9290b57cec5SDimitry Andric SVal Arg = M.getArgSVal(0); 9300b57cec5SDimitry Andric ProgramStateRef notNilState, nilState; 9310b57cec5SDimitry Andric std::tie(notNilState, nilState) = 9320b57cec5SDimitry Andric M.getState()->assume(Arg.castAs<DefinedOrUnknownSVal>()); 9330b57cec5SDimitry Andric if (!(nilState && !notNilState)) 9340b57cec5SDimitry Andric return nullptr; 9350b57cec5SDimitry Andric 9360b57cec5SDimitry Andric const ObjCPropertyDecl *Prop = M.getAccessedProperty(); 9370b57cec5SDimitry Andric if (!Prop) 9380b57cec5SDimitry Andric return nullptr; 9390b57cec5SDimitry Andric 9400b57cec5SDimitry Andric ObjCIvarDecl *PropIvarDecl = Prop->getPropertyIvarDecl(); 9410b57cec5SDimitry Andric if (!PropIvarDecl) 9420b57cec5SDimitry Andric return nullptr; 9430b57cec5SDimitry Andric 9440b57cec5SDimitry Andric ProgramStateRef State = C.getState(); 9450b57cec5SDimitry Andric 9460b57cec5SDimitry Andric SVal LVal = State->getLValue(PropIvarDecl, ReceiverVal); 947bdd1243dSDimitry Andric std::optional<Loc> LValLoc = LVal.getAs<Loc>(); 9480b57cec5SDimitry Andric if (!LValLoc) 9490b57cec5SDimitry Andric return nullptr; 9500b57cec5SDimitry Andric 95181ad6265SDimitry Andric SVal CurrentValInIvar = State->getSVal(*LValLoc); 9520b57cec5SDimitry Andric return CurrentValInIvar.getAsSymbol(); 9530b57cec5SDimitry Andric } 9540b57cec5SDimitry Andric 9550b57cec5SDimitry Andric /// Returns true if the current context is a call to -dealloc and false 9560b57cec5SDimitry Andric /// otherwise. If true, it also sets SelfValOut to the value of 9570b57cec5SDimitry Andric /// 'self'. 9580b57cec5SDimitry Andric bool ObjCDeallocChecker::isInInstanceDealloc(const CheckerContext &C, 9590b57cec5SDimitry Andric SVal &SelfValOut) const { 9600b57cec5SDimitry Andric return isInInstanceDealloc(C, C.getLocationContext(), SelfValOut); 9610b57cec5SDimitry Andric } 9620b57cec5SDimitry Andric 9630b57cec5SDimitry Andric /// Returns true if LCtx is a call to -dealloc and false 9640b57cec5SDimitry Andric /// otherwise. If true, it also sets SelfValOut to the value of 9650b57cec5SDimitry Andric /// 'self'. 9660b57cec5SDimitry Andric bool ObjCDeallocChecker::isInInstanceDealloc(const CheckerContext &C, 9670b57cec5SDimitry Andric const LocationContext *LCtx, 9680b57cec5SDimitry Andric SVal &SelfValOut) const { 9690b57cec5SDimitry Andric auto *MD = dyn_cast<ObjCMethodDecl>(LCtx->getDecl()); 9700b57cec5SDimitry Andric if (!MD || !MD->isInstanceMethod() || MD->getSelector() != DeallocSel) 9710b57cec5SDimitry Andric return false; 9720b57cec5SDimitry Andric 9730b57cec5SDimitry Andric const ImplicitParamDecl *SelfDecl = LCtx->getSelfDecl(); 9740b57cec5SDimitry Andric assert(SelfDecl && "No self in -dealloc?"); 9750b57cec5SDimitry Andric 9760b57cec5SDimitry Andric ProgramStateRef State = C.getState(); 9770b57cec5SDimitry Andric SelfValOut = State->getSVal(State->getRegion(SelfDecl, LCtx)); 9780b57cec5SDimitry Andric return true; 9790b57cec5SDimitry Andric } 9800b57cec5SDimitry Andric 9810b57cec5SDimitry Andric /// Returns true if there is a call to -dealloc anywhere on the stack and false 9820b57cec5SDimitry Andric /// otherwise. If true, it also sets InstanceValOut to the value of 9830b57cec5SDimitry Andric /// 'self' in the frame for -dealloc. 9840b57cec5SDimitry Andric bool ObjCDeallocChecker::instanceDeallocIsOnStack(const CheckerContext &C, 9850b57cec5SDimitry Andric SVal &InstanceValOut) const { 9860b57cec5SDimitry Andric const LocationContext *LCtx = C.getLocationContext(); 9870b57cec5SDimitry Andric 9880b57cec5SDimitry Andric while (LCtx) { 9890b57cec5SDimitry Andric if (isInInstanceDealloc(C, LCtx, InstanceValOut)) 9900b57cec5SDimitry Andric return true; 9910b57cec5SDimitry Andric 9920b57cec5SDimitry Andric LCtx = LCtx->getParent(); 9930b57cec5SDimitry Andric } 9940b57cec5SDimitry Andric 9950b57cec5SDimitry Andric return false; 9960b57cec5SDimitry Andric } 9970b57cec5SDimitry Andric 998bdd1243dSDimitry Andric /// Returns true if the ID is a class in which is known to have 9990b57cec5SDimitry Andric /// a separate teardown lifecycle. In this case, -dealloc warnings 10000b57cec5SDimitry Andric /// about missing releases should be suppressed. 10010b57cec5SDimitry Andric bool ObjCDeallocChecker::classHasSeparateTeardown( 10020b57cec5SDimitry Andric const ObjCInterfaceDecl *ID) const { 10030b57cec5SDimitry Andric // Suppress if the class is not a subclass of NSObject. 10040b57cec5SDimitry Andric for ( ; ID ; ID = ID->getSuperClass()) { 10050b57cec5SDimitry Andric IdentifierInfo *II = ID->getIdentifier(); 10060b57cec5SDimitry Andric 10070b57cec5SDimitry Andric if (II == NSObjectII) 10080b57cec5SDimitry Andric return false; 10090b57cec5SDimitry Andric 10100b57cec5SDimitry Andric // FIXME: For now, ignore classes that subclass SenTestCase and XCTestCase, 10110b57cec5SDimitry Andric // as these don't need to implement -dealloc. They implement tear down in 10120b57cec5SDimitry Andric // another way, which we should try and catch later. 10130b57cec5SDimitry Andric // http://llvm.org/bugs/show_bug.cgi?id=3187 10140b57cec5SDimitry Andric if (II == XCTestCaseII || II == SenTestCaseII) 10150b57cec5SDimitry Andric return true; 10160b57cec5SDimitry Andric } 10170b57cec5SDimitry Andric 10180b57cec5SDimitry Andric return true; 10190b57cec5SDimitry Andric } 10200b57cec5SDimitry Andric 10210b57cec5SDimitry Andric /// The -dealloc method in CIFilter highly unusual in that is will release 10220b57cec5SDimitry Andric /// instance variables belonging to its *subclasses* if the variable name 10230b57cec5SDimitry Andric /// starts with "input" or backs a property whose name starts with "input". 10240b57cec5SDimitry Andric /// Subclasses should not release these ivars in their own -dealloc method -- 10250b57cec5SDimitry Andric /// doing so could result in an over release. 10260b57cec5SDimitry Andric /// 10270b57cec5SDimitry Andric /// This method returns true if the property will be released by 10280b57cec5SDimitry Andric /// -[CIFilter dealloc]. 10290b57cec5SDimitry Andric bool ObjCDeallocChecker::isReleasedByCIFilterDealloc( 10300b57cec5SDimitry Andric const ObjCPropertyImplDecl *PropImpl) const { 10310b57cec5SDimitry Andric assert(PropImpl->getPropertyIvarDecl()); 10320b57cec5SDimitry Andric StringRef PropName = PropImpl->getPropertyDecl()->getName(); 10330b57cec5SDimitry Andric StringRef IvarName = PropImpl->getPropertyIvarDecl()->getName(); 10340b57cec5SDimitry Andric 10350b57cec5SDimitry Andric const char *ReleasePrefix = "input"; 10365f757f3fSDimitry Andric if (!(PropName.starts_with(ReleasePrefix) || 10375f757f3fSDimitry Andric IvarName.starts_with(ReleasePrefix))) { 10380b57cec5SDimitry Andric return false; 10390b57cec5SDimitry Andric } 10400b57cec5SDimitry Andric 10410b57cec5SDimitry Andric const ObjCInterfaceDecl *ID = 10420b57cec5SDimitry Andric PropImpl->getPropertyIvarDecl()->getContainingInterface(); 10430b57cec5SDimitry Andric for ( ; ID ; ID = ID->getSuperClass()) { 10440b57cec5SDimitry Andric IdentifierInfo *II = ID->getIdentifier(); 10450b57cec5SDimitry Andric if (II == CIFilterII) 10460b57cec5SDimitry Andric return true; 10470b57cec5SDimitry Andric } 10480b57cec5SDimitry Andric 10490b57cec5SDimitry Andric return false; 10500b57cec5SDimitry Andric } 10510b57cec5SDimitry Andric 10520b57cec5SDimitry Andric /// Returns whether the ivar backing the property is an IBOutlet that 10530b57cec5SDimitry Andric /// has its value set by nib loading code without retaining the value. 10540b57cec5SDimitry Andric /// 10550b57cec5SDimitry Andric /// On macOS, if there is no setter, the nib-loading code sets the ivar 10560b57cec5SDimitry Andric /// directly, without retaining the value, 10570b57cec5SDimitry Andric /// 10580b57cec5SDimitry Andric /// On iOS and its derivatives, the nib-loading code will call 10590b57cec5SDimitry Andric /// -setValue:forKey:, which retains the value before directly setting the ivar. 10600b57cec5SDimitry Andric bool ObjCDeallocChecker::isNibLoadedIvarWithoutRetain( 10610b57cec5SDimitry Andric const ObjCPropertyImplDecl *PropImpl) const { 10620b57cec5SDimitry Andric const ObjCIvarDecl *IvarDecl = PropImpl->getPropertyIvarDecl(); 10630b57cec5SDimitry Andric if (!IvarDecl->hasAttr<IBOutletAttr>()) 10640b57cec5SDimitry Andric return false; 10650b57cec5SDimitry Andric 10660b57cec5SDimitry Andric const llvm::Triple &Target = 10670b57cec5SDimitry Andric IvarDecl->getASTContext().getTargetInfo().getTriple(); 10680b57cec5SDimitry Andric 10690b57cec5SDimitry Andric if (!Target.isMacOSX()) 10700b57cec5SDimitry Andric return false; 10710b57cec5SDimitry Andric 10720b57cec5SDimitry Andric if (PropImpl->getPropertyDecl()->getSetterMethodDecl()) 10730b57cec5SDimitry Andric return false; 10740b57cec5SDimitry Andric 10750b57cec5SDimitry Andric return true; 10760b57cec5SDimitry Andric } 10770b57cec5SDimitry Andric 10780b57cec5SDimitry Andric void ento::registerObjCDeallocChecker(CheckerManager &Mgr) { 10790b57cec5SDimitry Andric Mgr.registerChecker<ObjCDeallocChecker>(); 10800b57cec5SDimitry Andric } 10810b57cec5SDimitry Andric 10825ffd83dbSDimitry Andric bool ento::shouldRegisterObjCDeallocChecker(const CheckerManager &mgr) { 10830b57cec5SDimitry Andric // These checker only makes sense under MRR. 10845ffd83dbSDimitry Andric const LangOptions &LO = mgr.getLangOpts(); 10850b57cec5SDimitry Andric return LO.getGC() != LangOptions::GCOnly && !LO.ObjCAutoRefCount; 10860b57cec5SDimitry Andric } 1087