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