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