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