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