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