xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp (revision 0b57cec536236d46e3dba9bd041533462f33dbb7)
1*0b57cec5SDimitry Andric //==- CheckObjCDealloc.cpp - Check ObjC -dealloc implementation --*- C++ -*-==//
2*0b57cec5SDimitry Andric //
3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*0b57cec5SDimitry Andric //
7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
8*0b57cec5SDimitry Andric //
9*0b57cec5SDimitry Andric //  This checker analyzes Objective-C -dealloc methods and their callees
10*0b57cec5SDimitry Andric //  to warn about improper releasing of instance variables that back synthesized
11*0b57cec5SDimitry Andric // properties. It warns about missing releases in the following cases:
12*0b57cec5SDimitry Andric //  - When a class has a synthesized instance variable for a 'retain' or 'copy'
13*0b57cec5SDimitry Andric //    property and lacks a -dealloc method in its implementation.
14*0b57cec5SDimitry Andric //  - When a class has a synthesized instance variable for a 'retain'/'copy'
15*0b57cec5SDimitry Andric //   property but the ivar is not released in -dealloc by either -release
16*0b57cec5SDimitry Andric //   or by nilling out the property.
17*0b57cec5SDimitry Andric //
18*0b57cec5SDimitry Andric //  It warns about extra releases in -dealloc (but not in callees) when a
19*0b57cec5SDimitry Andric //  synthesized instance variable is released in the following cases:
20*0b57cec5SDimitry Andric //  - When the property is 'assign' and is not 'readonly'.
21*0b57cec5SDimitry Andric //  - When the property is 'weak'.
22*0b57cec5SDimitry Andric //
23*0b57cec5SDimitry Andric //  This checker only warns for instance variables synthesized to back
24*0b57cec5SDimitry Andric //  properties. Handling the more general case would require inferring whether
25*0b57cec5SDimitry Andric //  an instance variable is stored retained or not. For synthesized properties,
26*0b57cec5SDimitry Andric //  this is specified in the property declaration itself.
27*0b57cec5SDimitry Andric //
28*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
29*0b57cec5SDimitry Andric 
30*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
31*0b57cec5SDimitry Andric #include "clang/AST/Attr.h"
32*0b57cec5SDimitry Andric #include "clang/AST/DeclObjC.h"
33*0b57cec5SDimitry Andric #include "clang/AST/Expr.h"
34*0b57cec5SDimitry Andric #include "clang/AST/ExprObjC.h"
35*0b57cec5SDimitry Andric #include "clang/Basic/LangOptions.h"
36*0b57cec5SDimitry Andric #include "clang/Basic/TargetInfo.h"
37*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
38*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
39*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
40*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
41*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
42*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
43*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
44*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
45*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
46*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
47*0b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
48*0b57cec5SDimitry Andric 
49*0b57cec5SDimitry Andric using namespace clang;
50*0b57cec5SDimitry Andric using namespace ento;
51*0b57cec5SDimitry Andric 
52*0b57cec5SDimitry Andric /// Indicates whether an instance variable is required to be released in
53*0b57cec5SDimitry Andric /// -dealloc.
54*0b57cec5SDimitry Andric enum class ReleaseRequirement {
55*0b57cec5SDimitry Andric   /// The instance variable must be released, either by calling
56*0b57cec5SDimitry Andric   /// -release on it directly or by nilling it out with a property setter.
57*0b57cec5SDimitry Andric   MustRelease,
58*0b57cec5SDimitry Andric 
59*0b57cec5SDimitry Andric   /// The instance variable must not be directly released with -release.
60*0b57cec5SDimitry Andric   MustNotReleaseDirectly,
61*0b57cec5SDimitry Andric 
62*0b57cec5SDimitry Andric   /// The requirement for the instance variable could not be determined.
63*0b57cec5SDimitry Andric   Unknown
64*0b57cec5SDimitry Andric };
65*0b57cec5SDimitry Andric 
66*0b57cec5SDimitry Andric /// Returns true if the property implementation is synthesized and the
67*0b57cec5SDimitry Andric /// type of the property is retainable.
68*0b57cec5SDimitry Andric static bool isSynthesizedRetainableProperty(const ObjCPropertyImplDecl *I,
69*0b57cec5SDimitry Andric                                             const ObjCIvarDecl **ID,
70*0b57cec5SDimitry Andric                                             const ObjCPropertyDecl **PD) {
71*0b57cec5SDimitry Andric 
72*0b57cec5SDimitry Andric   if (I->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize)
73*0b57cec5SDimitry Andric     return false;
74*0b57cec5SDimitry Andric 
75*0b57cec5SDimitry Andric   (*ID) = I->getPropertyIvarDecl();
76*0b57cec5SDimitry Andric   if (!(*ID))
77*0b57cec5SDimitry Andric     return false;
78*0b57cec5SDimitry Andric 
79*0b57cec5SDimitry Andric   QualType T = (*ID)->getType();
80*0b57cec5SDimitry Andric   if (!T->isObjCRetainableType())
81*0b57cec5SDimitry Andric     return false;
82*0b57cec5SDimitry Andric 
83*0b57cec5SDimitry Andric   (*PD) = I->getPropertyDecl();
84*0b57cec5SDimitry Andric   // Shouldn't be able to synthesize a property that doesn't exist.
85*0b57cec5SDimitry Andric   assert(*PD);
86*0b57cec5SDimitry Andric 
87*0b57cec5SDimitry Andric   return true;
88*0b57cec5SDimitry Andric }
89*0b57cec5SDimitry Andric 
90*0b57cec5SDimitry Andric namespace {
91*0b57cec5SDimitry Andric 
92*0b57cec5SDimitry Andric class ObjCDeallocChecker
93*0b57cec5SDimitry Andric     : public Checker<check::ASTDecl<ObjCImplementationDecl>,
94*0b57cec5SDimitry Andric                      check::PreObjCMessage, check::PostObjCMessage,
95*0b57cec5SDimitry Andric                      check::PreCall,
96*0b57cec5SDimitry Andric                      check::BeginFunction, check::EndFunction,
97*0b57cec5SDimitry Andric                      eval::Assume,
98*0b57cec5SDimitry Andric                      check::PointerEscape,
99*0b57cec5SDimitry Andric                      check::PreStmt<ReturnStmt>> {
100*0b57cec5SDimitry Andric 
101*0b57cec5SDimitry Andric   mutable IdentifierInfo *NSObjectII, *SenTestCaseII, *XCTestCaseII,
102*0b57cec5SDimitry Andric       *Block_releaseII, *CIFilterII;
103*0b57cec5SDimitry Andric 
104*0b57cec5SDimitry Andric   mutable Selector DeallocSel, ReleaseSel;
105*0b57cec5SDimitry Andric 
106*0b57cec5SDimitry Andric   std::unique_ptr<BugType> MissingReleaseBugType;
107*0b57cec5SDimitry Andric   std::unique_ptr<BugType> ExtraReleaseBugType;
108*0b57cec5SDimitry Andric   std::unique_ptr<BugType> MistakenDeallocBugType;
109*0b57cec5SDimitry Andric 
110*0b57cec5SDimitry Andric public:
111*0b57cec5SDimitry Andric   ObjCDeallocChecker();
112*0b57cec5SDimitry Andric 
113*0b57cec5SDimitry Andric   void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr,
114*0b57cec5SDimitry Andric                     BugReporter &BR) const;
115*0b57cec5SDimitry Andric   void checkBeginFunction(CheckerContext &Ctx) const;
116*0b57cec5SDimitry Andric   void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
117*0b57cec5SDimitry Andric   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
118*0b57cec5SDimitry Andric   void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
119*0b57cec5SDimitry Andric 
120*0b57cec5SDimitry Andric   ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond,
121*0b57cec5SDimitry Andric                              bool Assumption) const;
122*0b57cec5SDimitry Andric 
123*0b57cec5SDimitry Andric   ProgramStateRef checkPointerEscape(ProgramStateRef State,
124*0b57cec5SDimitry Andric                                      const InvalidatedSymbols &Escaped,
125*0b57cec5SDimitry Andric                                      const CallEvent *Call,
126*0b57cec5SDimitry Andric                                      PointerEscapeKind Kind) const;
127*0b57cec5SDimitry Andric   void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const;
128*0b57cec5SDimitry Andric   void checkEndFunction(const ReturnStmt *RS, CheckerContext &Ctx) const;
129*0b57cec5SDimitry Andric 
130*0b57cec5SDimitry Andric private:
131*0b57cec5SDimitry Andric   void diagnoseMissingReleases(CheckerContext &C) const;
132*0b57cec5SDimitry Andric 
133*0b57cec5SDimitry Andric   bool diagnoseExtraRelease(SymbolRef ReleasedValue, const ObjCMethodCall &M,
134*0b57cec5SDimitry Andric                             CheckerContext &C) const;
135*0b57cec5SDimitry Andric 
136*0b57cec5SDimitry Andric   bool diagnoseMistakenDealloc(SymbolRef DeallocedValue,
137*0b57cec5SDimitry Andric                                const ObjCMethodCall &M,
138*0b57cec5SDimitry Andric                                CheckerContext &C) const;
139*0b57cec5SDimitry Andric 
140*0b57cec5SDimitry Andric   SymbolRef getValueReleasedByNillingOut(const ObjCMethodCall &M,
141*0b57cec5SDimitry Andric                                          CheckerContext &C) const;
142*0b57cec5SDimitry Andric 
143*0b57cec5SDimitry Andric   const ObjCIvarRegion *getIvarRegionForIvarSymbol(SymbolRef IvarSym) const;
144*0b57cec5SDimitry Andric   SymbolRef getInstanceSymbolFromIvarSymbol(SymbolRef IvarSym) const;
145*0b57cec5SDimitry Andric 
146*0b57cec5SDimitry Andric   const ObjCPropertyImplDecl*
147*0b57cec5SDimitry Andric   findPropertyOnDeallocatingInstance(SymbolRef IvarSym,
148*0b57cec5SDimitry Andric                                      CheckerContext &C) const;
149*0b57cec5SDimitry Andric 
150*0b57cec5SDimitry Andric   ReleaseRequirement
151*0b57cec5SDimitry Andric   getDeallocReleaseRequirement(const ObjCPropertyImplDecl *PropImpl) const;
152*0b57cec5SDimitry Andric 
153*0b57cec5SDimitry Andric   bool isInInstanceDealloc(const CheckerContext &C, SVal &SelfValOut) const;
154*0b57cec5SDimitry Andric   bool isInInstanceDealloc(const CheckerContext &C, const LocationContext *LCtx,
155*0b57cec5SDimitry Andric                            SVal &SelfValOut) const;
156*0b57cec5SDimitry Andric   bool instanceDeallocIsOnStack(const CheckerContext &C,
157*0b57cec5SDimitry Andric                                 SVal &InstanceValOut) const;
158*0b57cec5SDimitry Andric 
159*0b57cec5SDimitry Andric   bool isSuperDeallocMessage(const ObjCMethodCall &M) const;
160*0b57cec5SDimitry Andric 
161*0b57cec5SDimitry Andric   const ObjCImplDecl *getContainingObjCImpl(const LocationContext *LCtx) const;
162*0b57cec5SDimitry Andric 
163*0b57cec5SDimitry Andric   const ObjCPropertyDecl *
164*0b57cec5SDimitry Andric   findShadowedPropertyDecl(const ObjCPropertyImplDecl *PropImpl) const;
165*0b57cec5SDimitry Andric 
166*0b57cec5SDimitry Andric   void transitionToReleaseValue(CheckerContext &C, SymbolRef Value) const;
167*0b57cec5SDimitry Andric   ProgramStateRef removeValueRequiringRelease(ProgramStateRef State,
168*0b57cec5SDimitry Andric                                               SymbolRef InstanceSym,
169*0b57cec5SDimitry Andric                                               SymbolRef ValueSym) const;
170*0b57cec5SDimitry Andric 
171*0b57cec5SDimitry Andric   void initIdentifierInfoAndSelectors(ASTContext &Ctx) const;
172*0b57cec5SDimitry Andric 
173*0b57cec5SDimitry Andric   bool classHasSeparateTeardown(const ObjCInterfaceDecl *ID) const;
174*0b57cec5SDimitry Andric 
175*0b57cec5SDimitry Andric   bool isReleasedByCIFilterDealloc(const ObjCPropertyImplDecl *PropImpl) const;
176*0b57cec5SDimitry Andric   bool isNibLoadedIvarWithoutRetain(const ObjCPropertyImplDecl *PropImpl) const;
177*0b57cec5SDimitry Andric };
178*0b57cec5SDimitry Andric } // End anonymous namespace.
179*0b57cec5SDimitry Andric 
180*0b57cec5SDimitry Andric 
181*0b57cec5SDimitry Andric /// Maps from the symbol for a class instance to the set of
182*0b57cec5SDimitry Andric /// symbols remaining that must be released in -dealloc.
183*0b57cec5SDimitry Andric REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(SymbolSet, SymbolRef)
184*0b57cec5SDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(UnreleasedIvarMap, SymbolRef, SymbolSet)
185*0b57cec5SDimitry Andric 
186*0b57cec5SDimitry Andric 
187*0b57cec5SDimitry Andric /// An AST check that diagnose when the class requires a -dealloc method and
188*0b57cec5SDimitry Andric /// is missing one.
189*0b57cec5SDimitry Andric void ObjCDeallocChecker::checkASTDecl(const ObjCImplementationDecl *D,
190*0b57cec5SDimitry Andric                                       AnalysisManager &Mgr,
191*0b57cec5SDimitry Andric                                       BugReporter &BR) const {
192*0b57cec5SDimitry Andric   assert(Mgr.getLangOpts().getGC() != LangOptions::GCOnly);
193*0b57cec5SDimitry Andric   assert(!Mgr.getLangOpts().ObjCAutoRefCount);
194*0b57cec5SDimitry Andric   initIdentifierInfoAndSelectors(Mgr.getASTContext());
195*0b57cec5SDimitry Andric 
196*0b57cec5SDimitry Andric   const ObjCInterfaceDecl *ID = D->getClassInterface();
197*0b57cec5SDimitry Andric   // If the class is known to have a lifecycle with a separate teardown method
198*0b57cec5SDimitry Andric   // then it may not require a -dealloc method.
199*0b57cec5SDimitry Andric   if (classHasSeparateTeardown(ID))
200*0b57cec5SDimitry Andric     return;
201*0b57cec5SDimitry Andric 
202*0b57cec5SDimitry Andric   // Does the class contain any synthesized properties that are retainable?
203*0b57cec5SDimitry Andric   // If not, skip the check entirely.
204*0b57cec5SDimitry Andric   const ObjCPropertyImplDecl *PropImplRequiringRelease = nullptr;
205*0b57cec5SDimitry Andric   bool HasOthers = false;
206*0b57cec5SDimitry Andric   for (const auto *I : D->property_impls()) {
207*0b57cec5SDimitry Andric     if (getDeallocReleaseRequirement(I) == ReleaseRequirement::MustRelease) {
208*0b57cec5SDimitry Andric       if (!PropImplRequiringRelease)
209*0b57cec5SDimitry Andric         PropImplRequiringRelease = I;
210*0b57cec5SDimitry Andric       else {
211*0b57cec5SDimitry Andric         HasOthers = true;
212*0b57cec5SDimitry Andric         break;
213*0b57cec5SDimitry Andric       }
214*0b57cec5SDimitry Andric     }
215*0b57cec5SDimitry Andric   }
216*0b57cec5SDimitry Andric 
217*0b57cec5SDimitry Andric   if (!PropImplRequiringRelease)
218*0b57cec5SDimitry Andric     return;
219*0b57cec5SDimitry Andric 
220*0b57cec5SDimitry Andric   const ObjCMethodDecl *MD = nullptr;
221*0b57cec5SDimitry Andric 
222*0b57cec5SDimitry Andric   // Scan the instance methods for "dealloc".
223*0b57cec5SDimitry Andric   for (const auto *I : D->instance_methods()) {
224*0b57cec5SDimitry Andric     if (I->getSelector() == DeallocSel) {
225*0b57cec5SDimitry Andric       MD = I;
226*0b57cec5SDimitry Andric       break;
227*0b57cec5SDimitry Andric     }
228*0b57cec5SDimitry Andric   }
229*0b57cec5SDimitry Andric 
230*0b57cec5SDimitry Andric   if (!MD) { // No dealloc found.
231*0b57cec5SDimitry Andric     const char* Name = "Missing -dealloc";
232*0b57cec5SDimitry Andric 
233*0b57cec5SDimitry Andric     std::string Buf;
234*0b57cec5SDimitry Andric     llvm::raw_string_ostream OS(Buf);
235*0b57cec5SDimitry Andric     OS << "'" << *D << "' lacks a 'dealloc' instance method but "
236*0b57cec5SDimitry Andric        << "must release '" << *PropImplRequiringRelease->getPropertyIvarDecl()
237*0b57cec5SDimitry Andric        << "'";
238*0b57cec5SDimitry Andric 
239*0b57cec5SDimitry Andric     if (HasOthers)
240*0b57cec5SDimitry Andric       OS << " and others";
241*0b57cec5SDimitry Andric     PathDiagnosticLocation DLoc =
242*0b57cec5SDimitry Andric         PathDiagnosticLocation::createBegin(D, BR.getSourceManager());
243*0b57cec5SDimitry Andric 
244*0b57cec5SDimitry Andric     BR.EmitBasicReport(D, this, Name, categories::CoreFoundationObjectiveC,
245*0b57cec5SDimitry Andric                        OS.str(), DLoc);
246*0b57cec5SDimitry Andric     return;
247*0b57cec5SDimitry Andric   }
248*0b57cec5SDimitry Andric }
249*0b57cec5SDimitry Andric 
250*0b57cec5SDimitry Andric /// If this is the beginning of -dealloc, mark the values initially stored in
251*0b57cec5SDimitry Andric /// instance variables that must be released by the end of -dealloc
252*0b57cec5SDimitry Andric /// as unreleased in the state.
253*0b57cec5SDimitry Andric void ObjCDeallocChecker::checkBeginFunction(
254*0b57cec5SDimitry Andric     CheckerContext &C) const {
255*0b57cec5SDimitry Andric   initIdentifierInfoAndSelectors(C.getASTContext());
256*0b57cec5SDimitry Andric 
257*0b57cec5SDimitry Andric   // Only do this if the current method is -dealloc.
258*0b57cec5SDimitry Andric   SVal SelfVal;
259*0b57cec5SDimitry Andric   if (!isInInstanceDealloc(C, SelfVal))
260*0b57cec5SDimitry Andric     return;
261*0b57cec5SDimitry Andric 
262*0b57cec5SDimitry Andric   SymbolRef SelfSymbol = SelfVal.getAsSymbol();
263*0b57cec5SDimitry Andric 
264*0b57cec5SDimitry Andric   const LocationContext *LCtx = C.getLocationContext();
265*0b57cec5SDimitry Andric   ProgramStateRef InitialState = C.getState();
266*0b57cec5SDimitry Andric 
267*0b57cec5SDimitry Andric   ProgramStateRef State = InitialState;
268*0b57cec5SDimitry Andric 
269*0b57cec5SDimitry Andric   SymbolSet::Factory &F = State->getStateManager().get_context<SymbolSet>();
270*0b57cec5SDimitry Andric 
271*0b57cec5SDimitry Andric   // Symbols that must be released by the end of the -dealloc;
272*0b57cec5SDimitry Andric   SymbolSet RequiredReleases = F.getEmptySet();
273*0b57cec5SDimitry Andric 
274*0b57cec5SDimitry Andric   // If we're an inlined -dealloc, we should add our symbols to the existing
275*0b57cec5SDimitry Andric   // set from our subclass.
276*0b57cec5SDimitry Andric   if (const SymbolSet *CurrSet = State->get<UnreleasedIvarMap>(SelfSymbol))
277*0b57cec5SDimitry Andric     RequiredReleases = *CurrSet;
278*0b57cec5SDimitry Andric 
279*0b57cec5SDimitry Andric   for (auto *PropImpl : getContainingObjCImpl(LCtx)->property_impls()) {
280*0b57cec5SDimitry Andric     ReleaseRequirement Requirement = getDeallocReleaseRequirement(PropImpl);
281*0b57cec5SDimitry Andric     if (Requirement != ReleaseRequirement::MustRelease)
282*0b57cec5SDimitry Andric       continue;
283*0b57cec5SDimitry Andric 
284*0b57cec5SDimitry Andric     SVal LVal = State->getLValue(PropImpl->getPropertyIvarDecl(), SelfVal);
285*0b57cec5SDimitry Andric     Optional<Loc> LValLoc = LVal.getAs<Loc>();
286*0b57cec5SDimitry Andric     if (!LValLoc)
287*0b57cec5SDimitry Andric       continue;
288*0b57cec5SDimitry Andric 
289*0b57cec5SDimitry Andric     SVal InitialVal = State->getSVal(LValLoc.getValue());
290*0b57cec5SDimitry Andric     SymbolRef Symbol = InitialVal.getAsSymbol();
291*0b57cec5SDimitry Andric     if (!Symbol || !isa<SymbolRegionValue>(Symbol))
292*0b57cec5SDimitry Andric       continue;
293*0b57cec5SDimitry Andric 
294*0b57cec5SDimitry Andric     // Mark the value as requiring a release.
295*0b57cec5SDimitry Andric     RequiredReleases = F.add(RequiredReleases, Symbol);
296*0b57cec5SDimitry Andric   }
297*0b57cec5SDimitry Andric 
298*0b57cec5SDimitry Andric   if (!RequiredReleases.isEmpty()) {
299*0b57cec5SDimitry Andric     State = State->set<UnreleasedIvarMap>(SelfSymbol, RequiredReleases);
300*0b57cec5SDimitry Andric   }
301*0b57cec5SDimitry Andric 
302*0b57cec5SDimitry Andric   if (State != InitialState) {
303*0b57cec5SDimitry Andric     C.addTransition(State);
304*0b57cec5SDimitry Andric   }
305*0b57cec5SDimitry Andric }
306*0b57cec5SDimitry Andric 
307*0b57cec5SDimitry Andric /// Given a symbol for an ivar, return the ivar region it was loaded from.
308*0b57cec5SDimitry Andric /// Returns nullptr if the instance symbol cannot be found.
309*0b57cec5SDimitry Andric const ObjCIvarRegion *
310*0b57cec5SDimitry Andric ObjCDeallocChecker::getIvarRegionForIvarSymbol(SymbolRef IvarSym) const {
311*0b57cec5SDimitry Andric   return dyn_cast_or_null<ObjCIvarRegion>(IvarSym->getOriginRegion());
312*0b57cec5SDimitry Andric }
313*0b57cec5SDimitry Andric 
314*0b57cec5SDimitry Andric /// Given a symbol for an ivar, return a symbol for the instance containing
315*0b57cec5SDimitry Andric /// the ivar. Returns nullptr if the instance symbol cannot be found.
316*0b57cec5SDimitry Andric SymbolRef
317*0b57cec5SDimitry Andric ObjCDeallocChecker::getInstanceSymbolFromIvarSymbol(SymbolRef IvarSym) const {
318*0b57cec5SDimitry Andric 
319*0b57cec5SDimitry Andric   const ObjCIvarRegion *IvarRegion = getIvarRegionForIvarSymbol(IvarSym);
320*0b57cec5SDimitry Andric   if (!IvarRegion)
321*0b57cec5SDimitry Andric     return nullptr;
322*0b57cec5SDimitry Andric 
323*0b57cec5SDimitry Andric   return IvarRegion->getSymbolicBase()->getSymbol();
324*0b57cec5SDimitry Andric }
325*0b57cec5SDimitry Andric 
326*0b57cec5SDimitry Andric /// If we are in -dealloc or -dealloc is on the stack, handle the call if it is
327*0b57cec5SDimitry Andric /// a release or a nilling-out property setter.
328*0b57cec5SDimitry Andric void ObjCDeallocChecker::checkPreObjCMessage(
329*0b57cec5SDimitry Andric     const ObjCMethodCall &M, CheckerContext &C) const {
330*0b57cec5SDimitry Andric   // Only run if -dealloc is on the stack.
331*0b57cec5SDimitry Andric   SVal DeallocedInstance;
332*0b57cec5SDimitry Andric   if (!instanceDeallocIsOnStack(C, DeallocedInstance))
333*0b57cec5SDimitry Andric     return;
334*0b57cec5SDimitry Andric 
335*0b57cec5SDimitry Andric   SymbolRef ReleasedValue = nullptr;
336*0b57cec5SDimitry Andric 
337*0b57cec5SDimitry Andric   if (M.getSelector() == ReleaseSel) {
338*0b57cec5SDimitry Andric     ReleasedValue = M.getReceiverSVal().getAsSymbol();
339*0b57cec5SDimitry Andric   } else if (M.getSelector() == DeallocSel && !M.isReceiverSelfOrSuper()) {
340*0b57cec5SDimitry Andric     if (diagnoseMistakenDealloc(M.getReceiverSVal().getAsSymbol(), M, C))
341*0b57cec5SDimitry Andric       return;
342*0b57cec5SDimitry Andric   }
343*0b57cec5SDimitry Andric 
344*0b57cec5SDimitry Andric   if (ReleasedValue) {
345*0b57cec5SDimitry Andric     // An instance variable symbol was released with -release:
346*0b57cec5SDimitry Andric     //    [_property release];
347*0b57cec5SDimitry Andric     if (diagnoseExtraRelease(ReleasedValue,M, C))
348*0b57cec5SDimitry Andric       return;
349*0b57cec5SDimitry Andric   } else {
350*0b57cec5SDimitry Andric     // An instance variable symbol was released nilling out its property:
351*0b57cec5SDimitry Andric     //    self.property = nil;
352*0b57cec5SDimitry Andric     ReleasedValue = getValueReleasedByNillingOut(M, C);
353*0b57cec5SDimitry Andric   }
354*0b57cec5SDimitry Andric 
355*0b57cec5SDimitry Andric   if (!ReleasedValue)
356*0b57cec5SDimitry Andric     return;
357*0b57cec5SDimitry Andric 
358*0b57cec5SDimitry Andric   transitionToReleaseValue(C, ReleasedValue);
359*0b57cec5SDimitry Andric }
360*0b57cec5SDimitry Andric 
361*0b57cec5SDimitry Andric /// If we are in -dealloc or -dealloc is on the stack, handle the call if it is
362*0b57cec5SDimitry Andric /// call to Block_release().
363*0b57cec5SDimitry Andric void ObjCDeallocChecker::checkPreCall(const CallEvent &Call,
364*0b57cec5SDimitry Andric                                       CheckerContext &C) const {
365*0b57cec5SDimitry Andric   const IdentifierInfo *II = Call.getCalleeIdentifier();
366*0b57cec5SDimitry Andric   if (II != Block_releaseII)
367*0b57cec5SDimitry Andric     return;
368*0b57cec5SDimitry Andric 
369*0b57cec5SDimitry Andric   if (Call.getNumArgs() != 1)
370*0b57cec5SDimitry Andric     return;
371*0b57cec5SDimitry Andric 
372*0b57cec5SDimitry Andric   SymbolRef ReleasedValue = Call.getArgSVal(0).getAsSymbol();
373*0b57cec5SDimitry Andric   if (!ReleasedValue)
374*0b57cec5SDimitry Andric     return;
375*0b57cec5SDimitry Andric 
376*0b57cec5SDimitry Andric   transitionToReleaseValue(C, ReleasedValue);
377*0b57cec5SDimitry Andric }
378*0b57cec5SDimitry Andric /// If the message was a call to '[super dealloc]', diagnose any missing
379*0b57cec5SDimitry Andric /// releases.
380*0b57cec5SDimitry Andric void ObjCDeallocChecker::checkPostObjCMessage(
381*0b57cec5SDimitry Andric     const ObjCMethodCall &M, CheckerContext &C) const {
382*0b57cec5SDimitry Andric   // We perform this check post-message so that if the super -dealloc
383*0b57cec5SDimitry Andric   // calls a helper method and that this class overrides, any ivars released in
384*0b57cec5SDimitry Andric   // the helper method will be recorded before checking.
385*0b57cec5SDimitry Andric   if (isSuperDeallocMessage(M))
386*0b57cec5SDimitry Andric     diagnoseMissingReleases(C);
387*0b57cec5SDimitry Andric }
388*0b57cec5SDimitry Andric 
389*0b57cec5SDimitry Andric /// Check for missing releases even when -dealloc does not call
390*0b57cec5SDimitry Andric /// '[super dealloc]'.
391*0b57cec5SDimitry Andric void ObjCDeallocChecker::checkEndFunction(
392*0b57cec5SDimitry Andric     const ReturnStmt *RS, CheckerContext &C) const {
393*0b57cec5SDimitry Andric   diagnoseMissingReleases(C);
394*0b57cec5SDimitry Andric }
395*0b57cec5SDimitry Andric 
396*0b57cec5SDimitry Andric /// Check for missing releases on early return.
397*0b57cec5SDimitry Andric void ObjCDeallocChecker::checkPreStmt(
398*0b57cec5SDimitry Andric     const ReturnStmt *RS, CheckerContext &C) const {
399*0b57cec5SDimitry Andric   diagnoseMissingReleases(C);
400*0b57cec5SDimitry Andric }
401*0b57cec5SDimitry Andric 
402*0b57cec5SDimitry Andric /// When a symbol is assumed to be nil, remove it from the set of symbols
403*0b57cec5SDimitry Andric /// require to be nil.
404*0b57cec5SDimitry Andric ProgramStateRef ObjCDeallocChecker::evalAssume(ProgramStateRef State, SVal Cond,
405*0b57cec5SDimitry Andric                                                bool Assumption) const {
406*0b57cec5SDimitry Andric   if (State->get<UnreleasedIvarMap>().isEmpty())
407*0b57cec5SDimitry Andric     return State;
408*0b57cec5SDimitry Andric 
409*0b57cec5SDimitry Andric   auto *CondBSE = dyn_cast_or_null<BinarySymExpr>(Cond.getAsSymExpr());
410*0b57cec5SDimitry Andric   if (!CondBSE)
411*0b57cec5SDimitry Andric     return State;
412*0b57cec5SDimitry Andric 
413*0b57cec5SDimitry Andric   BinaryOperator::Opcode OpCode = CondBSE->getOpcode();
414*0b57cec5SDimitry Andric   if (Assumption) {
415*0b57cec5SDimitry Andric     if (OpCode != BO_EQ)
416*0b57cec5SDimitry Andric       return State;
417*0b57cec5SDimitry Andric   } else {
418*0b57cec5SDimitry Andric     if (OpCode != BO_NE)
419*0b57cec5SDimitry Andric       return State;
420*0b57cec5SDimitry Andric   }
421*0b57cec5SDimitry Andric 
422*0b57cec5SDimitry Andric   SymbolRef NullSymbol = nullptr;
423*0b57cec5SDimitry Andric   if (auto *SIE = dyn_cast<SymIntExpr>(CondBSE)) {
424*0b57cec5SDimitry Andric     const llvm::APInt &RHS = SIE->getRHS();
425*0b57cec5SDimitry Andric     if (RHS != 0)
426*0b57cec5SDimitry Andric       return State;
427*0b57cec5SDimitry Andric     NullSymbol = SIE->getLHS();
428*0b57cec5SDimitry Andric   } else if (auto *SIE = dyn_cast<IntSymExpr>(CondBSE)) {
429*0b57cec5SDimitry Andric     const llvm::APInt &LHS = SIE->getLHS();
430*0b57cec5SDimitry Andric     if (LHS != 0)
431*0b57cec5SDimitry Andric       return State;
432*0b57cec5SDimitry Andric     NullSymbol = SIE->getRHS();
433*0b57cec5SDimitry Andric   } else {
434*0b57cec5SDimitry Andric     return State;
435*0b57cec5SDimitry Andric   }
436*0b57cec5SDimitry Andric 
437*0b57cec5SDimitry Andric   SymbolRef InstanceSymbol = getInstanceSymbolFromIvarSymbol(NullSymbol);
438*0b57cec5SDimitry Andric   if (!InstanceSymbol)
439*0b57cec5SDimitry Andric     return State;
440*0b57cec5SDimitry Andric 
441*0b57cec5SDimitry Andric   State = removeValueRequiringRelease(State, InstanceSymbol, NullSymbol);
442*0b57cec5SDimitry Andric 
443*0b57cec5SDimitry Andric   return State;
444*0b57cec5SDimitry Andric }
445*0b57cec5SDimitry Andric 
446*0b57cec5SDimitry Andric /// If a symbol escapes conservatively assume unseen code released it.
447*0b57cec5SDimitry Andric ProgramStateRef ObjCDeallocChecker::checkPointerEscape(
448*0b57cec5SDimitry Andric     ProgramStateRef State, const InvalidatedSymbols &Escaped,
449*0b57cec5SDimitry Andric     const CallEvent *Call, PointerEscapeKind Kind) const {
450*0b57cec5SDimitry Andric 
451*0b57cec5SDimitry Andric   if (State->get<UnreleasedIvarMap>().isEmpty())
452*0b57cec5SDimitry Andric     return State;
453*0b57cec5SDimitry Andric 
454*0b57cec5SDimitry Andric   // Don't treat calls to '[super dealloc]' as escaping for the purposes
455*0b57cec5SDimitry Andric   // of this checker. Because the checker diagnoses missing releases in the
456*0b57cec5SDimitry Andric   // post-message handler for '[super dealloc], escaping here would cause
457*0b57cec5SDimitry Andric   // the checker to never warn.
458*0b57cec5SDimitry Andric   auto *OMC = dyn_cast_or_null<ObjCMethodCall>(Call);
459*0b57cec5SDimitry Andric   if (OMC && isSuperDeallocMessage(*OMC))
460*0b57cec5SDimitry Andric     return State;
461*0b57cec5SDimitry Andric 
462*0b57cec5SDimitry Andric   for (const auto &Sym : Escaped) {
463*0b57cec5SDimitry Andric     if (!Call || (Call && !Call->isInSystemHeader())) {
464*0b57cec5SDimitry Andric       // If Sym is a symbol for an object with instance variables that
465*0b57cec5SDimitry Andric       // must be released, remove these obligations when the object escapes
466*0b57cec5SDimitry Andric       // unless via a call to a system function. System functions are
467*0b57cec5SDimitry Andric       // very unlikely to release instance variables on objects passed to them,
468*0b57cec5SDimitry Andric       // and are frequently called on 'self' in -dealloc (e.g., to remove
469*0b57cec5SDimitry Andric       // observers) -- we want to avoid false negatives from escaping on
470*0b57cec5SDimitry Andric       // them.
471*0b57cec5SDimitry Andric       State = State->remove<UnreleasedIvarMap>(Sym);
472*0b57cec5SDimitry Andric     }
473*0b57cec5SDimitry Andric 
474*0b57cec5SDimitry Andric 
475*0b57cec5SDimitry Andric     SymbolRef InstanceSymbol = getInstanceSymbolFromIvarSymbol(Sym);
476*0b57cec5SDimitry Andric     if (!InstanceSymbol)
477*0b57cec5SDimitry Andric       continue;
478*0b57cec5SDimitry Andric 
479*0b57cec5SDimitry Andric     State = removeValueRequiringRelease(State, InstanceSymbol, Sym);
480*0b57cec5SDimitry Andric   }
481*0b57cec5SDimitry Andric 
482*0b57cec5SDimitry Andric   return State;
483*0b57cec5SDimitry Andric }
484*0b57cec5SDimitry Andric 
485*0b57cec5SDimitry Andric /// Report any unreleased instance variables for the current instance being
486*0b57cec5SDimitry Andric /// dealloced.
487*0b57cec5SDimitry Andric void ObjCDeallocChecker::diagnoseMissingReleases(CheckerContext &C) const {
488*0b57cec5SDimitry Andric   ProgramStateRef State = C.getState();
489*0b57cec5SDimitry Andric 
490*0b57cec5SDimitry Andric   SVal SelfVal;
491*0b57cec5SDimitry Andric   if (!isInInstanceDealloc(C, SelfVal))
492*0b57cec5SDimitry Andric     return;
493*0b57cec5SDimitry Andric 
494*0b57cec5SDimitry Andric   const MemRegion *SelfRegion = SelfVal.castAs<loc::MemRegionVal>().getRegion();
495*0b57cec5SDimitry Andric   const LocationContext *LCtx = C.getLocationContext();
496*0b57cec5SDimitry Andric 
497*0b57cec5SDimitry Andric   ExplodedNode *ErrNode = nullptr;
498*0b57cec5SDimitry Andric 
499*0b57cec5SDimitry Andric   SymbolRef SelfSym = SelfVal.getAsSymbol();
500*0b57cec5SDimitry Andric   if (!SelfSym)
501*0b57cec5SDimitry Andric     return;
502*0b57cec5SDimitry Andric 
503*0b57cec5SDimitry Andric   const SymbolSet *OldUnreleased = State->get<UnreleasedIvarMap>(SelfSym);
504*0b57cec5SDimitry Andric   if (!OldUnreleased)
505*0b57cec5SDimitry Andric     return;
506*0b57cec5SDimitry Andric 
507*0b57cec5SDimitry Andric   SymbolSet NewUnreleased = *OldUnreleased;
508*0b57cec5SDimitry Andric   SymbolSet::Factory &F = State->getStateManager().get_context<SymbolSet>();
509*0b57cec5SDimitry Andric 
510*0b57cec5SDimitry Andric   ProgramStateRef InitialState = State;
511*0b57cec5SDimitry Andric 
512*0b57cec5SDimitry Andric   for (auto *IvarSymbol : *OldUnreleased) {
513*0b57cec5SDimitry Andric     const TypedValueRegion *TVR =
514*0b57cec5SDimitry Andric         cast<SymbolRegionValue>(IvarSymbol)->getRegion();
515*0b57cec5SDimitry Andric     const ObjCIvarRegion *IvarRegion = cast<ObjCIvarRegion>(TVR);
516*0b57cec5SDimitry Andric 
517*0b57cec5SDimitry Andric     // Don't warn if the ivar is not for this instance.
518*0b57cec5SDimitry Andric     if (SelfRegion != IvarRegion->getSuperRegion())
519*0b57cec5SDimitry Andric       continue;
520*0b57cec5SDimitry Andric 
521*0b57cec5SDimitry Andric     const ObjCIvarDecl *IvarDecl = IvarRegion->getDecl();
522*0b57cec5SDimitry Andric     // Prevent an inlined call to -dealloc in a super class from warning
523*0b57cec5SDimitry Andric     // about the values the subclass's -dealloc should release.
524*0b57cec5SDimitry Andric     if (IvarDecl->getContainingInterface() !=
525*0b57cec5SDimitry Andric         cast<ObjCMethodDecl>(LCtx->getDecl())->getClassInterface())
526*0b57cec5SDimitry Andric       continue;
527*0b57cec5SDimitry Andric 
528*0b57cec5SDimitry Andric     // Prevents diagnosing multiple times for the same instance variable
529*0b57cec5SDimitry Andric     // at, for example, both a return and at the end of the function.
530*0b57cec5SDimitry Andric     NewUnreleased = F.remove(NewUnreleased, IvarSymbol);
531*0b57cec5SDimitry Andric 
532*0b57cec5SDimitry Andric     if (State->getStateManager()
533*0b57cec5SDimitry Andric             .getConstraintManager()
534*0b57cec5SDimitry Andric             .isNull(State, IvarSymbol)
535*0b57cec5SDimitry Andric             .isConstrainedTrue()) {
536*0b57cec5SDimitry Andric       continue;
537*0b57cec5SDimitry Andric     }
538*0b57cec5SDimitry Andric 
539*0b57cec5SDimitry Andric     // A missing release manifests as a leak, so treat as a non-fatal error.
540*0b57cec5SDimitry Andric     if (!ErrNode)
541*0b57cec5SDimitry Andric       ErrNode = C.generateNonFatalErrorNode();
542*0b57cec5SDimitry Andric     // If we've already reached this node on another path, return without
543*0b57cec5SDimitry Andric     // diagnosing.
544*0b57cec5SDimitry Andric     if (!ErrNode)
545*0b57cec5SDimitry Andric       return;
546*0b57cec5SDimitry Andric 
547*0b57cec5SDimitry Andric     std::string Buf;
548*0b57cec5SDimitry Andric     llvm::raw_string_ostream OS(Buf);
549*0b57cec5SDimitry Andric 
550*0b57cec5SDimitry Andric     const ObjCInterfaceDecl *Interface = IvarDecl->getContainingInterface();
551*0b57cec5SDimitry Andric     // If the class is known to have a lifecycle with teardown that is
552*0b57cec5SDimitry Andric     // separate from -dealloc, do not warn about missing releases. We
553*0b57cec5SDimitry Andric     // suppress here (rather than not tracking for instance variables in
554*0b57cec5SDimitry Andric     // such classes) because these classes are rare.
555*0b57cec5SDimitry Andric     if (classHasSeparateTeardown(Interface))
556*0b57cec5SDimitry Andric       return;
557*0b57cec5SDimitry Andric 
558*0b57cec5SDimitry Andric     ObjCImplDecl *ImplDecl = Interface->getImplementation();
559*0b57cec5SDimitry Andric 
560*0b57cec5SDimitry Andric     const ObjCPropertyImplDecl *PropImpl =
561*0b57cec5SDimitry Andric         ImplDecl->FindPropertyImplIvarDecl(IvarDecl->getIdentifier());
562*0b57cec5SDimitry Andric 
563*0b57cec5SDimitry Andric     const ObjCPropertyDecl *PropDecl = PropImpl->getPropertyDecl();
564*0b57cec5SDimitry Andric 
565*0b57cec5SDimitry Andric     assert(PropDecl->getSetterKind() == ObjCPropertyDecl::Copy ||
566*0b57cec5SDimitry Andric            PropDecl->getSetterKind() == ObjCPropertyDecl::Retain);
567*0b57cec5SDimitry Andric 
568*0b57cec5SDimitry Andric     OS << "The '" << *IvarDecl << "' ivar in '" << *ImplDecl
569*0b57cec5SDimitry Andric        << "' was ";
570*0b57cec5SDimitry Andric 
571*0b57cec5SDimitry Andric     if (PropDecl->getSetterKind() == ObjCPropertyDecl::Retain)
572*0b57cec5SDimitry Andric       OS << "retained";
573*0b57cec5SDimitry Andric     else
574*0b57cec5SDimitry Andric       OS << "copied";
575*0b57cec5SDimitry Andric 
576*0b57cec5SDimitry Andric     OS << " by a synthesized property but not released"
577*0b57cec5SDimitry Andric           " before '[super dealloc]'";
578*0b57cec5SDimitry Andric 
579*0b57cec5SDimitry Andric     std::unique_ptr<BugReport> BR(
580*0b57cec5SDimitry Andric         new BugReport(*MissingReleaseBugType, OS.str(), ErrNode));
581*0b57cec5SDimitry Andric 
582*0b57cec5SDimitry Andric     C.emitReport(std::move(BR));
583*0b57cec5SDimitry Andric   }
584*0b57cec5SDimitry Andric 
585*0b57cec5SDimitry Andric   if (NewUnreleased.isEmpty()) {
586*0b57cec5SDimitry Andric     State = State->remove<UnreleasedIvarMap>(SelfSym);
587*0b57cec5SDimitry Andric   } else {
588*0b57cec5SDimitry Andric     State = State->set<UnreleasedIvarMap>(SelfSym, NewUnreleased);
589*0b57cec5SDimitry Andric   }
590*0b57cec5SDimitry Andric 
591*0b57cec5SDimitry Andric   if (ErrNode) {
592*0b57cec5SDimitry Andric     C.addTransition(State, ErrNode);
593*0b57cec5SDimitry Andric   } else if (State != InitialState) {
594*0b57cec5SDimitry Andric     C.addTransition(State);
595*0b57cec5SDimitry Andric   }
596*0b57cec5SDimitry Andric 
597*0b57cec5SDimitry Andric   // Make sure that after checking in the top-most frame the list of
598*0b57cec5SDimitry Andric   // tracked ivars is empty. This is intended to detect accidental leaks in
599*0b57cec5SDimitry Andric   // the UnreleasedIvarMap program state.
600*0b57cec5SDimitry Andric   assert(!LCtx->inTopFrame() || State->get<UnreleasedIvarMap>().isEmpty());
601*0b57cec5SDimitry Andric }
602*0b57cec5SDimitry Andric 
603*0b57cec5SDimitry Andric /// Given a symbol, determine whether the symbol refers to an ivar on
604*0b57cec5SDimitry Andric /// the top-most deallocating instance. If so, find the property for that
605*0b57cec5SDimitry Andric /// ivar, if one exists. Otherwise return null.
606*0b57cec5SDimitry Andric const ObjCPropertyImplDecl *
607*0b57cec5SDimitry Andric ObjCDeallocChecker::findPropertyOnDeallocatingInstance(
608*0b57cec5SDimitry Andric     SymbolRef IvarSym, CheckerContext &C) const {
609*0b57cec5SDimitry Andric   SVal DeallocedInstance;
610*0b57cec5SDimitry Andric   if (!isInInstanceDealloc(C, DeallocedInstance))
611*0b57cec5SDimitry Andric     return nullptr;
612*0b57cec5SDimitry Andric 
613*0b57cec5SDimitry Andric   // Try to get the region from which the ivar value was loaded.
614*0b57cec5SDimitry Andric   auto *IvarRegion = getIvarRegionForIvarSymbol(IvarSym);
615*0b57cec5SDimitry Andric   if (!IvarRegion)
616*0b57cec5SDimitry Andric     return nullptr;
617*0b57cec5SDimitry Andric 
618*0b57cec5SDimitry Andric   // Don't try to find the property if the ivar was not loaded from the
619*0b57cec5SDimitry Andric   // given instance.
620*0b57cec5SDimitry Andric   if (DeallocedInstance.castAs<loc::MemRegionVal>().getRegion() !=
621*0b57cec5SDimitry Andric       IvarRegion->getSuperRegion())
622*0b57cec5SDimitry Andric     return nullptr;
623*0b57cec5SDimitry Andric 
624*0b57cec5SDimitry Andric   const LocationContext *LCtx = C.getLocationContext();
625*0b57cec5SDimitry Andric   const ObjCIvarDecl *IvarDecl = IvarRegion->getDecl();
626*0b57cec5SDimitry Andric 
627*0b57cec5SDimitry Andric   const ObjCImplDecl *Container = getContainingObjCImpl(LCtx);
628*0b57cec5SDimitry Andric   const ObjCPropertyImplDecl *PropImpl =
629*0b57cec5SDimitry Andric       Container->FindPropertyImplIvarDecl(IvarDecl->getIdentifier());
630*0b57cec5SDimitry Andric   return PropImpl;
631*0b57cec5SDimitry Andric }
632*0b57cec5SDimitry Andric 
633*0b57cec5SDimitry Andric /// Emits a warning if the current context is -dealloc and ReleasedValue
634*0b57cec5SDimitry Andric /// must not be directly released in a -dealloc. Returns true if a diagnostic
635*0b57cec5SDimitry Andric /// was emitted.
636*0b57cec5SDimitry Andric bool ObjCDeallocChecker::diagnoseExtraRelease(SymbolRef ReleasedValue,
637*0b57cec5SDimitry Andric                                               const ObjCMethodCall &M,
638*0b57cec5SDimitry Andric                                               CheckerContext &C) const {
639*0b57cec5SDimitry Andric   // Try to get the region from which the released value was loaded.
640*0b57cec5SDimitry Andric   // Note that, unlike diagnosing for missing releases, here we don't track
641*0b57cec5SDimitry Andric   // values that must not be released in the state. This is because even if
642*0b57cec5SDimitry Andric   // these values escape, it is still an error under the rules of MRR to
643*0b57cec5SDimitry Andric   // release them in -dealloc.
644*0b57cec5SDimitry Andric   const ObjCPropertyImplDecl *PropImpl =
645*0b57cec5SDimitry Andric       findPropertyOnDeallocatingInstance(ReleasedValue, C);
646*0b57cec5SDimitry Andric 
647*0b57cec5SDimitry Andric   if (!PropImpl)
648*0b57cec5SDimitry Andric     return false;
649*0b57cec5SDimitry Andric 
650*0b57cec5SDimitry Andric   // If the ivar belongs to a property that must not be released directly
651*0b57cec5SDimitry Andric   // in dealloc, emit a warning.
652*0b57cec5SDimitry Andric   if (getDeallocReleaseRequirement(PropImpl) !=
653*0b57cec5SDimitry Andric       ReleaseRequirement::MustNotReleaseDirectly) {
654*0b57cec5SDimitry Andric     return false;
655*0b57cec5SDimitry Andric   }
656*0b57cec5SDimitry Andric 
657*0b57cec5SDimitry Andric   // If the property is readwrite but it shadows a read-only property in its
658*0b57cec5SDimitry Andric   // external interface, treat the property a read-only. If the outside
659*0b57cec5SDimitry Andric   // world cannot write to a property then the internal implementation is free
660*0b57cec5SDimitry Andric   // to make its own convention about whether the value is stored retained
661*0b57cec5SDimitry Andric   // or not. We look up the shadow here rather than in
662*0b57cec5SDimitry Andric   // getDeallocReleaseRequirement() because doing so can be expensive.
663*0b57cec5SDimitry Andric   const ObjCPropertyDecl *PropDecl = findShadowedPropertyDecl(PropImpl);
664*0b57cec5SDimitry Andric   if (PropDecl) {
665*0b57cec5SDimitry Andric     if (PropDecl->isReadOnly())
666*0b57cec5SDimitry Andric       return false;
667*0b57cec5SDimitry Andric   } else {
668*0b57cec5SDimitry Andric     PropDecl = PropImpl->getPropertyDecl();
669*0b57cec5SDimitry Andric   }
670*0b57cec5SDimitry Andric 
671*0b57cec5SDimitry Andric   ExplodedNode *ErrNode = C.generateNonFatalErrorNode();
672*0b57cec5SDimitry Andric   if (!ErrNode)
673*0b57cec5SDimitry Andric     return false;
674*0b57cec5SDimitry Andric 
675*0b57cec5SDimitry Andric   std::string Buf;
676*0b57cec5SDimitry Andric   llvm::raw_string_ostream OS(Buf);
677*0b57cec5SDimitry Andric 
678*0b57cec5SDimitry Andric   assert(PropDecl->getSetterKind() == ObjCPropertyDecl::Weak ||
679*0b57cec5SDimitry Andric          (PropDecl->getSetterKind() == ObjCPropertyDecl::Assign &&
680*0b57cec5SDimitry Andric           !PropDecl->isReadOnly()) ||
681*0b57cec5SDimitry Andric          isReleasedByCIFilterDealloc(PropImpl)
682*0b57cec5SDimitry Andric          );
683*0b57cec5SDimitry Andric 
684*0b57cec5SDimitry Andric   const ObjCImplDecl *Container = getContainingObjCImpl(C.getLocationContext());
685*0b57cec5SDimitry Andric   OS << "The '" << *PropImpl->getPropertyIvarDecl()
686*0b57cec5SDimitry Andric      << "' ivar in '" << *Container;
687*0b57cec5SDimitry Andric 
688*0b57cec5SDimitry Andric 
689*0b57cec5SDimitry Andric   if (isReleasedByCIFilterDealloc(PropImpl)) {
690*0b57cec5SDimitry Andric     OS << "' will be released by '-[CIFilter dealloc]' but also released here";
691*0b57cec5SDimitry Andric   } else {
692*0b57cec5SDimitry Andric     OS << "' was synthesized for ";
693*0b57cec5SDimitry Andric 
694*0b57cec5SDimitry Andric     if (PropDecl->getSetterKind() == ObjCPropertyDecl::Weak)
695*0b57cec5SDimitry Andric       OS << "a weak";
696*0b57cec5SDimitry Andric     else
697*0b57cec5SDimitry Andric       OS << "an assign, readwrite";
698*0b57cec5SDimitry Andric 
699*0b57cec5SDimitry Andric     OS <<  " property but was released in 'dealloc'";
700*0b57cec5SDimitry Andric   }
701*0b57cec5SDimitry Andric 
702*0b57cec5SDimitry Andric   std::unique_ptr<BugReport> BR(
703*0b57cec5SDimitry Andric       new BugReport(*ExtraReleaseBugType, OS.str(), ErrNode));
704*0b57cec5SDimitry Andric   BR->addRange(M.getOriginExpr()->getSourceRange());
705*0b57cec5SDimitry Andric 
706*0b57cec5SDimitry Andric   C.emitReport(std::move(BR));
707*0b57cec5SDimitry Andric 
708*0b57cec5SDimitry Andric   return true;
709*0b57cec5SDimitry Andric }
710*0b57cec5SDimitry Andric 
711*0b57cec5SDimitry Andric /// Emits a warning if the current context is -dealloc and DeallocedValue
712*0b57cec5SDimitry Andric /// must not be directly dealloced in a -dealloc. Returns true if a diagnostic
713*0b57cec5SDimitry Andric /// was emitted.
714*0b57cec5SDimitry Andric bool ObjCDeallocChecker::diagnoseMistakenDealloc(SymbolRef DeallocedValue,
715*0b57cec5SDimitry Andric                                                  const ObjCMethodCall &M,
716*0b57cec5SDimitry Andric                                                  CheckerContext &C) const {
717*0b57cec5SDimitry Andric   // TODO: Apart from unknown/undefined receivers, this may happen when
718*0b57cec5SDimitry Andric   // dealloc is called as a class method. Should we warn?
719*0b57cec5SDimitry Andric   if (!DeallocedValue)
720*0b57cec5SDimitry Andric     return false;
721*0b57cec5SDimitry Andric 
722*0b57cec5SDimitry Andric   // Find the property backing the instance variable that M
723*0b57cec5SDimitry Andric   // is dealloc'ing.
724*0b57cec5SDimitry Andric   const ObjCPropertyImplDecl *PropImpl =
725*0b57cec5SDimitry Andric       findPropertyOnDeallocatingInstance(DeallocedValue, C);
726*0b57cec5SDimitry Andric   if (!PropImpl)
727*0b57cec5SDimitry Andric     return false;
728*0b57cec5SDimitry Andric 
729*0b57cec5SDimitry Andric   if (getDeallocReleaseRequirement(PropImpl) !=
730*0b57cec5SDimitry Andric       ReleaseRequirement::MustRelease) {
731*0b57cec5SDimitry Andric     return false;
732*0b57cec5SDimitry Andric   }
733*0b57cec5SDimitry Andric 
734*0b57cec5SDimitry Andric   ExplodedNode *ErrNode = C.generateErrorNode();
735*0b57cec5SDimitry Andric   if (!ErrNode)
736*0b57cec5SDimitry Andric     return false;
737*0b57cec5SDimitry Andric 
738*0b57cec5SDimitry Andric   std::string Buf;
739*0b57cec5SDimitry Andric   llvm::raw_string_ostream OS(Buf);
740*0b57cec5SDimitry Andric 
741*0b57cec5SDimitry Andric   OS << "'" << *PropImpl->getPropertyIvarDecl()
742*0b57cec5SDimitry Andric      << "' should be released rather than deallocated";
743*0b57cec5SDimitry Andric 
744*0b57cec5SDimitry Andric   std::unique_ptr<BugReport> BR(
745*0b57cec5SDimitry Andric       new BugReport(*MistakenDeallocBugType, OS.str(), ErrNode));
746*0b57cec5SDimitry Andric   BR->addRange(M.getOriginExpr()->getSourceRange());
747*0b57cec5SDimitry Andric 
748*0b57cec5SDimitry Andric   C.emitReport(std::move(BR));
749*0b57cec5SDimitry Andric 
750*0b57cec5SDimitry Andric   return true;
751*0b57cec5SDimitry Andric }
752*0b57cec5SDimitry Andric 
753*0b57cec5SDimitry Andric ObjCDeallocChecker::ObjCDeallocChecker()
754*0b57cec5SDimitry Andric     : NSObjectII(nullptr), SenTestCaseII(nullptr), XCTestCaseII(nullptr),
755*0b57cec5SDimitry Andric       CIFilterII(nullptr) {
756*0b57cec5SDimitry Andric 
757*0b57cec5SDimitry Andric   MissingReleaseBugType.reset(
758*0b57cec5SDimitry Andric       new BugType(this, "Missing ivar release (leak)",
759*0b57cec5SDimitry Andric                   categories::MemoryRefCount));
760*0b57cec5SDimitry Andric 
761*0b57cec5SDimitry Andric   ExtraReleaseBugType.reset(
762*0b57cec5SDimitry Andric       new BugType(this, "Extra ivar release",
763*0b57cec5SDimitry Andric                   categories::MemoryRefCount));
764*0b57cec5SDimitry Andric 
765*0b57cec5SDimitry Andric   MistakenDeallocBugType.reset(
766*0b57cec5SDimitry Andric       new BugType(this, "Mistaken dealloc",
767*0b57cec5SDimitry Andric                   categories::MemoryRefCount));
768*0b57cec5SDimitry Andric }
769*0b57cec5SDimitry Andric 
770*0b57cec5SDimitry Andric void ObjCDeallocChecker::initIdentifierInfoAndSelectors(
771*0b57cec5SDimitry Andric     ASTContext &Ctx) const {
772*0b57cec5SDimitry Andric   if (NSObjectII)
773*0b57cec5SDimitry Andric     return;
774*0b57cec5SDimitry Andric 
775*0b57cec5SDimitry Andric   NSObjectII = &Ctx.Idents.get("NSObject");
776*0b57cec5SDimitry Andric   SenTestCaseII = &Ctx.Idents.get("SenTestCase");
777*0b57cec5SDimitry Andric   XCTestCaseII = &Ctx.Idents.get("XCTestCase");
778*0b57cec5SDimitry Andric   Block_releaseII = &Ctx.Idents.get("_Block_release");
779*0b57cec5SDimitry Andric   CIFilterII = &Ctx.Idents.get("CIFilter");
780*0b57cec5SDimitry Andric 
781*0b57cec5SDimitry Andric   IdentifierInfo *DeallocII = &Ctx.Idents.get("dealloc");
782*0b57cec5SDimitry Andric   IdentifierInfo *ReleaseII = &Ctx.Idents.get("release");
783*0b57cec5SDimitry Andric   DeallocSel = Ctx.Selectors.getSelector(0, &DeallocII);
784*0b57cec5SDimitry Andric   ReleaseSel = Ctx.Selectors.getSelector(0, &ReleaseII);
785*0b57cec5SDimitry Andric }
786*0b57cec5SDimitry Andric 
787*0b57cec5SDimitry Andric /// Returns true if M is a call to '[super dealloc]'.
788*0b57cec5SDimitry Andric bool ObjCDeallocChecker::isSuperDeallocMessage(
789*0b57cec5SDimitry Andric     const ObjCMethodCall &M) const {
790*0b57cec5SDimitry Andric   if (M.getOriginExpr()->getReceiverKind() != ObjCMessageExpr::SuperInstance)
791*0b57cec5SDimitry Andric     return false;
792*0b57cec5SDimitry Andric 
793*0b57cec5SDimitry Andric   return M.getSelector() == DeallocSel;
794*0b57cec5SDimitry Andric }
795*0b57cec5SDimitry Andric 
796*0b57cec5SDimitry Andric /// Returns the ObjCImplDecl containing the method declaration in LCtx.
797*0b57cec5SDimitry Andric const ObjCImplDecl *
798*0b57cec5SDimitry Andric ObjCDeallocChecker::getContainingObjCImpl(const LocationContext *LCtx) const {
799*0b57cec5SDimitry Andric   auto *MD = cast<ObjCMethodDecl>(LCtx->getDecl());
800*0b57cec5SDimitry Andric   return cast<ObjCImplDecl>(MD->getDeclContext());
801*0b57cec5SDimitry Andric }
802*0b57cec5SDimitry Andric 
803*0b57cec5SDimitry Andric /// Returns the property that shadowed by PropImpl if one exists and
804*0b57cec5SDimitry Andric /// nullptr otherwise.
805*0b57cec5SDimitry Andric const ObjCPropertyDecl *ObjCDeallocChecker::findShadowedPropertyDecl(
806*0b57cec5SDimitry Andric     const ObjCPropertyImplDecl *PropImpl) const {
807*0b57cec5SDimitry Andric   const ObjCPropertyDecl *PropDecl = PropImpl->getPropertyDecl();
808*0b57cec5SDimitry Andric 
809*0b57cec5SDimitry Andric   // Only readwrite properties can shadow.
810*0b57cec5SDimitry Andric   if (PropDecl->isReadOnly())
811*0b57cec5SDimitry Andric     return nullptr;
812*0b57cec5SDimitry Andric 
813*0b57cec5SDimitry Andric   auto *CatDecl = dyn_cast<ObjCCategoryDecl>(PropDecl->getDeclContext());
814*0b57cec5SDimitry Andric 
815*0b57cec5SDimitry Andric   // Only class extensions can contain shadowing properties.
816*0b57cec5SDimitry Andric   if (!CatDecl || !CatDecl->IsClassExtension())
817*0b57cec5SDimitry Andric     return nullptr;
818*0b57cec5SDimitry Andric 
819*0b57cec5SDimitry Andric   IdentifierInfo *ID = PropDecl->getIdentifier();
820*0b57cec5SDimitry Andric   DeclContext::lookup_result R = CatDecl->getClassInterface()->lookup(ID);
821*0b57cec5SDimitry Andric   for (DeclContext::lookup_iterator I = R.begin(), E = R.end(); I != E; ++I) {
822*0b57cec5SDimitry Andric     auto *ShadowedPropDecl = dyn_cast<ObjCPropertyDecl>(*I);
823*0b57cec5SDimitry Andric     if (!ShadowedPropDecl)
824*0b57cec5SDimitry Andric       continue;
825*0b57cec5SDimitry Andric 
826*0b57cec5SDimitry Andric     if (ShadowedPropDecl->isInstanceProperty()) {
827*0b57cec5SDimitry Andric       assert(ShadowedPropDecl->isReadOnly());
828*0b57cec5SDimitry Andric       return ShadowedPropDecl;
829*0b57cec5SDimitry Andric     }
830*0b57cec5SDimitry Andric   }
831*0b57cec5SDimitry Andric 
832*0b57cec5SDimitry Andric   return nullptr;
833*0b57cec5SDimitry Andric }
834*0b57cec5SDimitry Andric 
835*0b57cec5SDimitry Andric /// Add a transition noting the release of the given value.
836*0b57cec5SDimitry Andric void ObjCDeallocChecker::transitionToReleaseValue(CheckerContext &C,
837*0b57cec5SDimitry Andric                                                   SymbolRef Value) const {
838*0b57cec5SDimitry Andric   assert(Value);
839*0b57cec5SDimitry Andric   SymbolRef InstanceSym = getInstanceSymbolFromIvarSymbol(Value);
840*0b57cec5SDimitry Andric   if (!InstanceSym)
841*0b57cec5SDimitry Andric     return;
842*0b57cec5SDimitry Andric   ProgramStateRef InitialState = C.getState();
843*0b57cec5SDimitry Andric 
844*0b57cec5SDimitry Andric   ProgramStateRef ReleasedState =
845*0b57cec5SDimitry Andric       removeValueRequiringRelease(InitialState, InstanceSym, Value);
846*0b57cec5SDimitry Andric 
847*0b57cec5SDimitry Andric   if (ReleasedState != InitialState) {
848*0b57cec5SDimitry Andric     C.addTransition(ReleasedState);
849*0b57cec5SDimitry Andric   }
850*0b57cec5SDimitry Andric }
851*0b57cec5SDimitry Andric 
852*0b57cec5SDimitry Andric /// Remove the Value requiring a release from the tracked set for
853*0b57cec5SDimitry Andric /// Instance and return the resultant state.
854*0b57cec5SDimitry Andric ProgramStateRef ObjCDeallocChecker::removeValueRequiringRelease(
855*0b57cec5SDimitry Andric     ProgramStateRef State, SymbolRef Instance, SymbolRef Value) const {
856*0b57cec5SDimitry Andric   assert(Instance);
857*0b57cec5SDimitry Andric   assert(Value);
858*0b57cec5SDimitry Andric   const ObjCIvarRegion *RemovedRegion = getIvarRegionForIvarSymbol(Value);
859*0b57cec5SDimitry Andric   if (!RemovedRegion)
860*0b57cec5SDimitry Andric     return State;
861*0b57cec5SDimitry Andric 
862*0b57cec5SDimitry Andric   const SymbolSet *Unreleased = State->get<UnreleasedIvarMap>(Instance);
863*0b57cec5SDimitry Andric   if (!Unreleased)
864*0b57cec5SDimitry Andric     return State;
865*0b57cec5SDimitry Andric 
866*0b57cec5SDimitry Andric   // Mark the value as no longer requiring a release.
867*0b57cec5SDimitry Andric   SymbolSet::Factory &F = State->getStateManager().get_context<SymbolSet>();
868*0b57cec5SDimitry Andric   SymbolSet NewUnreleased = *Unreleased;
869*0b57cec5SDimitry Andric   for (auto &Sym : *Unreleased) {
870*0b57cec5SDimitry Andric     const ObjCIvarRegion *UnreleasedRegion = getIvarRegionForIvarSymbol(Sym);
871*0b57cec5SDimitry Andric     assert(UnreleasedRegion);
872*0b57cec5SDimitry Andric     if (RemovedRegion->getDecl() == UnreleasedRegion->getDecl()) {
873*0b57cec5SDimitry Andric       NewUnreleased = F.remove(NewUnreleased, Sym);
874*0b57cec5SDimitry Andric     }
875*0b57cec5SDimitry Andric   }
876*0b57cec5SDimitry Andric 
877*0b57cec5SDimitry Andric   if (NewUnreleased.isEmpty()) {
878*0b57cec5SDimitry Andric     return State->remove<UnreleasedIvarMap>(Instance);
879*0b57cec5SDimitry Andric   }
880*0b57cec5SDimitry Andric 
881*0b57cec5SDimitry Andric   return State->set<UnreleasedIvarMap>(Instance, NewUnreleased);
882*0b57cec5SDimitry Andric }
883*0b57cec5SDimitry Andric 
884*0b57cec5SDimitry Andric /// Determines whether the instance variable for \p PropImpl must or must not be
885*0b57cec5SDimitry Andric /// released in -dealloc or whether it cannot be determined.
886*0b57cec5SDimitry Andric ReleaseRequirement ObjCDeallocChecker::getDeallocReleaseRequirement(
887*0b57cec5SDimitry Andric     const ObjCPropertyImplDecl *PropImpl) const {
888*0b57cec5SDimitry Andric   const ObjCIvarDecl *IvarDecl;
889*0b57cec5SDimitry Andric   const ObjCPropertyDecl *PropDecl;
890*0b57cec5SDimitry Andric   if (!isSynthesizedRetainableProperty(PropImpl, &IvarDecl, &PropDecl))
891*0b57cec5SDimitry Andric     return ReleaseRequirement::Unknown;
892*0b57cec5SDimitry Andric 
893*0b57cec5SDimitry Andric   ObjCPropertyDecl::SetterKind SK = PropDecl->getSetterKind();
894*0b57cec5SDimitry Andric 
895*0b57cec5SDimitry Andric   switch (SK) {
896*0b57cec5SDimitry Andric   // Retain and copy setters retain/copy their values before storing and so
897*0b57cec5SDimitry Andric   // the value in their instance variables must be released in -dealloc.
898*0b57cec5SDimitry Andric   case ObjCPropertyDecl::Retain:
899*0b57cec5SDimitry Andric   case ObjCPropertyDecl::Copy:
900*0b57cec5SDimitry Andric     if (isReleasedByCIFilterDealloc(PropImpl))
901*0b57cec5SDimitry Andric       return ReleaseRequirement::MustNotReleaseDirectly;
902*0b57cec5SDimitry Andric 
903*0b57cec5SDimitry Andric     if (isNibLoadedIvarWithoutRetain(PropImpl))
904*0b57cec5SDimitry Andric       return ReleaseRequirement::Unknown;
905*0b57cec5SDimitry Andric 
906*0b57cec5SDimitry Andric     return ReleaseRequirement::MustRelease;
907*0b57cec5SDimitry Andric 
908*0b57cec5SDimitry Andric   case ObjCPropertyDecl::Weak:
909*0b57cec5SDimitry Andric     return ReleaseRequirement::MustNotReleaseDirectly;
910*0b57cec5SDimitry Andric 
911*0b57cec5SDimitry Andric   case ObjCPropertyDecl::Assign:
912*0b57cec5SDimitry Andric     // It is common for the ivars for read-only assign properties to
913*0b57cec5SDimitry Andric     // always be stored retained, so their release requirement cannot be
914*0b57cec5SDimitry Andric     // be determined.
915*0b57cec5SDimitry Andric     if (PropDecl->isReadOnly())
916*0b57cec5SDimitry Andric       return ReleaseRequirement::Unknown;
917*0b57cec5SDimitry Andric 
918*0b57cec5SDimitry Andric     return ReleaseRequirement::MustNotReleaseDirectly;
919*0b57cec5SDimitry Andric   }
920*0b57cec5SDimitry Andric   llvm_unreachable("Unrecognized setter kind");
921*0b57cec5SDimitry Andric }
922*0b57cec5SDimitry Andric 
923*0b57cec5SDimitry Andric /// Returns the released value if M is a call a setter that releases
924*0b57cec5SDimitry Andric /// and nils out its underlying instance variable.
925*0b57cec5SDimitry Andric SymbolRef
926*0b57cec5SDimitry Andric ObjCDeallocChecker::getValueReleasedByNillingOut(const ObjCMethodCall &M,
927*0b57cec5SDimitry Andric                                                  CheckerContext &C) const {
928*0b57cec5SDimitry Andric   SVal ReceiverVal = M.getReceiverSVal();
929*0b57cec5SDimitry Andric   if (!ReceiverVal.isValid())
930*0b57cec5SDimitry Andric     return nullptr;
931*0b57cec5SDimitry Andric 
932*0b57cec5SDimitry Andric   if (M.getNumArgs() == 0)
933*0b57cec5SDimitry Andric     return nullptr;
934*0b57cec5SDimitry Andric 
935*0b57cec5SDimitry Andric   if (!M.getArgExpr(0)->getType()->isObjCRetainableType())
936*0b57cec5SDimitry Andric     return nullptr;
937*0b57cec5SDimitry Andric 
938*0b57cec5SDimitry Andric   // Is the first argument nil?
939*0b57cec5SDimitry Andric   SVal Arg = M.getArgSVal(0);
940*0b57cec5SDimitry Andric   ProgramStateRef notNilState, nilState;
941*0b57cec5SDimitry Andric   std::tie(notNilState, nilState) =
942*0b57cec5SDimitry Andric       M.getState()->assume(Arg.castAs<DefinedOrUnknownSVal>());
943*0b57cec5SDimitry Andric   if (!(nilState && !notNilState))
944*0b57cec5SDimitry Andric     return nullptr;
945*0b57cec5SDimitry Andric 
946*0b57cec5SDimitry Andric   const ObjCPropertyDecl *Prop = M.getAccessedProperty();
947*0b57cec5SDimitry Andric   if (!Prop)
948*0b57cec5SDimitry Andric     return nullptr;
949*0b57cec5SDimitry Andric 
950*0b57cec5SDimitry Andric   ObjCIvarDecl *PropIvarDecl = Prop->getPropertyIvarDecl();
951*0b57cec5SDimitry Andric   if (!PropIvarDecl)
952*0b57cec5SDimitry Andric     return nullptr;
953*0b57cec5SDimitry Andric 
954*0b57cec5SDimitry Andric   ProgramStateRef State = C.getState();
955*0b57cec5SDimitry Andric 
956*0b57cec5SDimitry Andric   SVal LVal = State->getLValue(PropIvarDecl, ReceiverVal);
957*0b57cec5SDimitry Andric   Optional<Loc> LValLoc = LVal.getAs<Loc>();
958*0b57cec5SDimitry Andric   if (!LValLoc)
959*0b57cec5SDimitry Andric     return nullptr;
960*0b57cec5SDimitry Andric 
961*0b57cec5SDimitry Andric   SVal CurrentValInIvar = State->getSVal(LValLoc.getValue());
962*0b57cec5SDimitry Andric   return CurrentValInIvar.getAsSymbol();
963*0b57cec5SDimitry Andric }
964*0b57cec5SDimitry Andric 
965*0b57cec5SDimitry Andric /// Returns true if the current context is a call to -dealloc and false
966*0b57cec5SDimitry Andric /// otherwise. If true, it also sets SelfValOut to the value of
967*0b57cec5SDimitry Andric /// 'self'.
968*0b57cec5SDimitry Andric bool ObjCDeallocChecker::isInInstanceDealloc(const CheckerContext &C,
969*0b57cec5SDimitry Andric                                              SVal &SelfValOut) const {
970*0b57cec5SDimitry Andric   return isInInstanceDealloc(C, C.getLocationContext(), SelfValOut);
971*0b57cec5SDimitry Andric }
972*0b57cec5SDimitry Andric 
973*0b57cec5SDimitry Andric /// Returns true if LCtx is a call to -dealloc and false
974*0b57cec5SDimitry Andric /// otherwise. If true, it also sets SelfValOut to the value of
975*0b57cec5SDimitry Andric /// 'self'.
976*0b57cec5SDimitry Andric bool ObjCDeallocChecker::isInInstanceDealloc(const CheckerContext &C,
977*0b57cec5SDimitry Andric                                              const LocationContext *LCtx,
978*0b57cec5SDimitry Andric                                              SVal &SelfValOut) const {
979*0b57cec5SDimitry Andric   auto *MD = dyn_cast<ObjCMethodDecl>(LCtx->getDecl());
980*0b57cec5SDimitry Andric   if (!MD || !MD->isInstanceMethod() || MD->getSelector() != DeallocSel)
981*0b57cec5SDimitry Andric     return false;
982*0b57cec5SDimitry Andric 
983*0b57cec5SDimitry Andric   const ImplicitParamDecl *SelfDecl = LCtx->getSelfDecl();
984*0b57cec5SDimitry Andric   assert(SelfDecl && "No self in -dealloc?");
985*0b57cec5SDimitry Andric 
986*0b57cec5SDimitry Andric   ProgramStateRef State = C.getState();
987*0b57cec5SDimitry Andric   SelfValOut = State->getSVal(State->getRegion(SelfDecl, LCtx));
988*0b57cec5SDimitry Andric   return true;
989*0b57cec5SDimitry Andric }
990*0b57cec5SDimitry Andric 
991*0b57cec5SDimitry Andric /// Returns true if there is a call to -dealloc anywhere on the stack and false
992*0b57cec5SDimitry Andric /// otherwise. If true, it also sets InstanceValOut to the value of
993*0b57cec5SDimitry Andric /// 'self' in the frame for -dealloc.
994*0b57cec5SDimitry Andric bool ObjCDeallocChecker::instanceDeallocIsOnStack(const CheckerContext &C,
995*0b57cec5SDimitry Andric                                                   SVal &InstanceValOut) const {
996*0b57cec5SDimitry Andric   const LocationContext *LCtx = C.getLocationContext();
997*0b57cec5SDimitry Andric 
998*0b57cec5SDimitry Andric   while (LCtx) {
999*0b57cec5SDimitry Andric     if (isInInstanceDealloc(C, LCtx, InstanceValOut))
1000*0b57cec5SDimitry Andric       return true;
1001*0b57cec5SDimitry Andric 
1002*0b57cec5SDimitry Andric     LCtx = LCtx->getParent();
1003*0b57cec5SDimitry Andric   }
1004*0b57cec5SDimitry Andric 
1005*0b57cec5SDimitry Andric   return false;
1006*0b57cec5SDimitry Andric }
1007*0b57cec5SDimitry Andric 
1008*0b57cec5SDimitry Andric /// Returns true if the ID is a class in which which is known to have
1009*0b57cec5SDimitry Andric /// a separate teardown lifecycle. In this case, -dealloc warnings
1010*0b57cec5SDimitry Andric /// about missing releases should be suppressed.
1011*0b57cec5SDimitry Andric bool ObjCDeallocChecker::classHasSeparateTeardown(
1012*0b57cec5SDimitry Andric     const ObjCInterfaceDecl *ID) const {
1013*0b57cec5SDimitry Andric   // Suppress if the class is not a subclass of NSObject.
1014*0b57cec5SDimitry Andric   for ( ; ID ; ID = ID->getSuperClass()) {
1015*0b57cec5SDimitry Andric     IdentifierInfo *II = ID->getIdentifier();
1016*0b57cec5SDimitry Andric 
1017*0b57cec5SDimitry Andric     if (II == NSObjectII)
1018*0b57cec5SDimitry Andric       return false;
1019*0b57cec5SDimitry Andric 
1020*0b57cec5SDimitry Andric     // FIXME: For now, ignore classes that subclass SenTestCase and XCTestCase,
1021*0b57cec5SDimitry Andric     // as these don't need to implement -dealloc.  They implement tear down in
1022*0b57cec5SDimitry Andric     // another way, which we should try and catch later.
1023*0b57cec5SDimitry Andric     //  http://llvm.org/bugs/show_bug.cgi?id=3187
1024*0b57cec5SDimitry Andric     if (II == XCTestCaseII || II == SenTestCaseII)
1025*0b57cec5SDimitry Andric       return true;
1026*0b57cec5SDimitry Andric   }
1027*0b57cec5SDimitry Andric 
1028*0b57cec5SDimitry Andric   return true;
1029*0b57cec5SDimitry Andric }
1030*0b57cec5SDimitry Andric 
1031*0b57cec5SDimitry Andric /// The -dealloc method in CIFilter highly unusual in that is will release
1032*0b57cec5SDimitry Andric /// instance variables belonging to its *subclasses* if the variable name
1033*0b57cec5SDimitry Andric /// starts with "input" or backs a property whose name starts with "input".
1034*0b57cec5SDimitry Andric /// Subclasses should not release these ivars in their own -dealloc method --
1035*0b57cec5SDimitry Andric /// doing so could result in an over release.
1036*0b57cec5SDimitry Andric ///
1037*0b57cec5SDimitry Andric /// This method returns true if the property will be released by
1038*0b57cec5SDimitry Andric /// -[CIFilter dealloc].
1039*0b57cec5SDimitry Andric bool ObjCDeallocChecker::isReleasedByCIFilterDealloc(
1040*0b57cec5SDimitry Andric     const ObjCPropertyImplDecl *PropImpl) const {
1041*0b57cec5SDimitry Andric   assert(PropImpl->getPropertyIvarDecl());
1042*0b57cec5SDimitry Andric   StringRef PropName = PropImpl->getPropertyDecl()->getName();
1043*0b57cec5SDimitry Andric   StringRef IvarName = PropImpl->getPropertyIvarDecl()->getName();
1044*0b57cec5SDimitry Andric 
1045*0b57cec5SDimitry Andric   const char *ReleasePrefix = "input";
1046*0b57cec5SDimitry Andric   if (!(PropName.startswith(ReleasePrefix) ||
1047*0b57cec5SDimitry Andric         IvarName.startswith(ReleasePrefix))) {
1048*0b57cec5SDimitry Andric     return false;
1049*0b57cec5SDimitry Andric   }
1050*0b57cec5SDimitry Andric 
1051*0b57cec5SDimitry Andric   const ObjCInterfaceDecl *ID =
1052*0b57cec5SDimitry Andric       PropImpl->getPropertyIvarDecl()->getContainingInterface();
1053*0b57cec5SDimitry Andric   for ( ; ID ; ID = ID->getSuperClass()) {
1054*0b57cec5SDimitry Andric     IdentifierInfo *II = ID->getIdentifier();
1055*0b57cec5SDimitry Andric     if (II == CIFilterII)
1056*0b57cec5SDimitry Andric       return true;
1057*0b57cec5SDimitry Andric   }
1058*0b57cec5SDimitry Andric 
1059*0b57cec5SDimitry Andric   return false;
1060*0b57cec5SDimitry Andric }
1061*0b57cec5SDimitry Andric 
1062*0b57cec5SDimitry Andric /// Returns whether the ivar backing the property is an IBOutlet that
1063*0b57cec5SDimitry Andric /// has its value set by nib loading code without retaining the value.
1064*0b57cec5SDimitry Andric ///
1065*0b57cec5SDimitry Andric /// On macOS, if there is no setter, the nib-loading code sets the ivar
1066*0b57cec5SDimitry Andric /// directly, without retaining the value,
1067*0b57cec5SDimitry Andric ///
1068*0b57cec5SDimitry Andric /// On iOS and its derivatives, the nib-loading code will call
1069*0b57cec5SDimitry Andric /// -setValue:forKey:, which retains the value before directly setting the ivar.
1070*0b57cec5SDimitry Andric bool ObjCDeallocChecker::isNibLoadedIvarWithoutRetain(
1071*0b57cec5SDimitry Andric     const ObjCPropertyImplDecl *PropImpl) const {
1072*0b57cec5SDimitry Andric   const ObjCIvarDecl *IvarDecl = PropImpl->getPropertyIvarDecl();
1073*0b57cec5SDimitry Andric   if (!IvarDecl->hasAttr<IBOutletAttr>())
1074*0b57cec5SDimitry Andric     return false;
1075*0b57cec5SDimitry Andric 
1076*0b57cec5SDimitry Andric   const llvm::Triple &Target =
1077*0b57cec5SDimitry Andric       IvarDecl->getASTContext().getTargetInfo().getTriple();
1078*0b57cec5SDimitry Andric 
1079*0b57cec5SDimitry Andric   if (!Target.isMacOSX())
1080*0b57cec5SDimitry Andric     return false;
1081*0b57cec5SDimitry Andric 
1082*0b57cec5SDimitry Andric   if (PropImpl->getPropertyDecl()->getSetterMethodDecl())
1083*0b57cec5SDimitry Andric     return false;
1084*0b57cec5SDimitry Andric 
1085*0b57cec5SDimitry Andric   return true;
1086*0b57cec5SDimitry Andric }
1087*0b57cec5SDimitry Andric 
1088*0b57cec5SDimitry Andric void ento::registerObjCDeallocChecker(CheckerManager &Mgr) {
1089*0b57cec5SDimitry Andric   Mgr.registerChecker<ObjCDeallocChecker>();
1090*0b57cec5SDimitry Andric }
1091*0b57cec5SDimitry Andric 
1092*0b57cec5SDimitry Andric bool ento::shouldRegisterObjCDeallocChecker(const LangOptions &LO) {
1093*0b57cec5SDimitry Andric   // These checker only makes sense under MRR.
1094*0b57cec5SDimitry Andric   return LO.getGC() != LangOptions::GCOnly && !LO.ObjCAutoRefCount;
1095*0b57cec5SDimitry Andric }
1096