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