xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp (revision 3075134739edc9cd16d5ca7b9a78d8b4d5fbff3e)
1 //==- CheckObjCDealloc.cpp - Check ObjC -dealloc implementation --*- C++ -*-==//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 //  This file defines a CheckObjCDealloc, a checker that
11 //  analyzes an Objective-C class's implementation to determine if it
12 //  correctly implements -dealloc.
13 //
14 //===----------------------------------------------------------------------===//
15 
16 #include "ClangSACheckers.h"
17 #include "clang/AST/Attr.h"
18 #include "clang/AST/DeclObjC.h"
19 #include "clang/AST/Expr.h"
20 #include "clang/AST/ExprObjC.h"
21 #include "clang/Basic/LangOptions.h"
22 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
23 #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
24 #include "clang/StaticAnalyzer/Core/Checker.h"
25 #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
26 #include "llvm/Support/raw_ostream.h"
27 
28 using namespace clang;
29 using namespace ento;
30 
31 static bool scan_ivar_release(Stmt *S, const ObjCIvarDecl *ID,
32                               const ObjCPropertyDecl *PD,
33                               Selector Release,
34                               IdentifierInfo* SelfII,
35                               ASTContext &Ctx) {
36 
37   // [mMyIvar release]
38   if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S))
39     if (ME->getSelector() == Release)
40       if (ME->getInstanceReceiver())
41         if (Expr *Receiver = ME->getInstanceReceiver()->IgnoreParenCasts())
42           if (ObjCIvarRefExpr *E = dyn_cast<ObjCIvarRefExpr>(Receiver))
43             if (E->getDecl() == ID)
44               return true;
45 
46   // [self setMyIvar:nil];
47   if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S))
48     if (ME->getInstanceReceiver())
49       if (Expr *Receiver = ME->getInstanceReceiver()->IgnoreParenCasts())
50         if (DeclRefExpr *E = dyn_cast<DeclRefExpr>(Receiver))
51           if (E->getDecl()->getIdentifier() == SelfII)
52             if (ME->getMethodDecl() == PD->getSetterMethodDecl() &&
53                 ME->getNumArgs() == 1 &&
54                 ME->getArg(0)->isNullPointerConstant(Ctx,
55                                               Expr::NPC_ValueDependentIsNull))
56               return true;
57 
58   // self.myIvar = nil;
59   if (BinaryOperator* BO = dyn_cast<BinaryOperator>(S))
60     if (BO->isAssignmentOp())
61       if (ObjCPropertyRefExpr *PRE =
62            dyn_cast<ObjCPropertyRefExpr>(BO->getLHS()->IgnoreParenCasts()))
63         if (PRE->isExplicitProperty() && PRE->getExplicitProperty() == PD)
64             if (BO->getRHS()->isNullPointerConstant(Ctx,
65                                             Expr::NPC_ValueDependentIsNull)) {
66               // This is only a 'release' if the property kind is not
67               // 'assign'.
68               return PD->getSetterKind() != ObjCPropertyDecl::Assign;
69             }
70 
71   // Recurse to children.
72   for (Stmt *SubStmt : S->children())
73     if (SubStmt && scan_ivar_release(SubStmt, ID, PD, Release, SelfII, Ctx))
74       return true;
75 
76   return false;
77 }
78 
79 static bool isSynthesizedRetainableProperty(const ObjCPropertyImplDecl *I,
80                                             const ObjCIvarDecl **ID,
81                                             const ObjCPropertyDecl **PD) {
82 
83   if (I->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize)
84     return false;
85 
86   (*ID) = I->getPropertyIvarDecl();
87   if (!(*ID))
88     return false;
89 
90   QualType T = (*ID)->getType();
91   if (!T->isObjCRetainableType())
92     return false;
93 
94   (*PD) = I->getPropertyDecl();
95   // Shouldn't be able to synthesize a property that doesn't exist.
96   assert(*PD);
97 
98   return true;
99 }
100 
101 static bool synthesizedPropertyRequiresRelease(const ObjCPropertyDecl *PD) {
102   // A synthesized property must be released if and only if the kind of setter
103   // was neither 'assign' or 'weak'.
104   ObjCPropertyDecl::SetterKind SK = PD->getSetterKind();
105   return (SK != ObjCPropertyDecl::Assign && SK != ObjCPropertyDecl::Weak);
106 }
107 
108 static void checkObjCDealloc(const CheckerBase *Checker,
109                              const ObjCImplementationDecl *D,
110                              const LangOptions &LOpts, BugReporter &BR) {
111 
112   assert(LOpts.getGC() != LangOptions::GCOnly);
113   assert(!LOpts.ObjCAutoRefCount);
114 
115   ASTContext &Ctx = BR.getContext();
116   const ObjCInterfaceDecl *ID = D->getClassInterface();
117 
118   // Does the class contain any synthesized properties that are retainable?
119   // If not, skip the check entirely.
120   bool containsRetainedSynthesizedProperty = false;
121   for (const auto *I : D->property_impls()) {
122     const ObjCIvarDecl *ID = nullptr;
123     const ObjCPropertyDecl *PD = nullptr;
124     if (!isSynthesizedRetainableProperty(I, &ID, &PD))
125       continue;
126 
127     if (synthesizedPropertyRequiresRelease(PD)) {
128       containsRetainedSynthesizedProperty = true;
129       break;
130     }
131   }
132 
133   if (!containsRetainedSynthesizedProperty)
134     return;
135 
136   // Determine if the class subclasses NSObject.
137   IdentifierInfo* NSObjectII = &Ctx.Idents.get("NSObject");
138   IdentifierInfo* SenTestCaseII = &Ctx.Idents.get("SenTestCase");
139 
140   for ( ; ID ; ID = ID->getSuperClass()) {
141     IdentifierInfo *II = ID->getIdentifier();
142 
143     if (II == NSObjectII)
144       break;
145 
146     // FIXME: For now, ignore classes that subclass SenTestCase, as these don't
147     // need to implement -dealloc.  They implement tear down in another way,
148     // which we should try and catch later.
149     //  http://llvm.org/bugs/show_bug.cgi?id=3187
150     if (II == SenTestCaseII)
151       return;
152   }
153 
154   if (!ID)
155     return;
156 
157   // Get the "dealloc" selector.
158   IdentifierInfo* II = &Ctx.Idents.get("dealloc");
159   Selector S = Ctx.Selectors.getSelector(0, &II);
160   const ObjCMethodDecl *MD = nullptr;
161 
162   // Scan the instance methods for "dealloc".
163   for (const auto *I : D->instance_methods()) {
164     if (I->getSelector() == S) {
165       MD = I;
166       break;
167     }
168   }
169 
170   if (!MD) { // No dealloc found.
171 
172     const char* name = LOpts.getGC() == LangOptions::NonGC
173                        ? "missing -dealloc"
174                        : "missing -dealloc (Hybrid MM, non-GC)";
175 
176     std::string buf;
177     llvm::raw_string_ostream os(buf);
178     os << "Objective-C class '" << *D << "' lacks a 'dealloc' instance method";
179 
180     PathDiagnosticLocation DLoc =
181         PathDiagnosticLocation::createBegin(D, BR.getSourceManager());
182 
183     BR.EmitBasicReport(D, Checker, name, categories::CoreFoundationObjectiveC,
184                        os.str(), DLoc);
185     return;
186   }
187 
188   // Get the "release" selector.
189   IdentifierInfo* RII = &Ctx.Idents.get("release");
190   Selector RS = Ctx.Selectors.getSelector(0, &RII);
191 
192   // Get the "self" identifier
193   IdentifierInfo* SelfII = &Ctx.Idents.get("self");
194 
195   // Scan for missing and extra releases of ivars used by implementations
196   // of synthesized properties
197   for (const auto *I : D->property_impls()) {
198     const ObjCIvarDecl *ID = nullptr;
199     const ObjCPropertyDecl *PD = nullptr;
200     if (!isSynthesizedRetainableProperty(I, &ID, &PD))
201       continue;
202 
203     bool requiresRelease = synthesizedPropertyRequiresRelease(PD);
204     if (scan_ivar_release(MD->getBody(), ID, PD, RS, SelfII, Ctx)
205        != requiresRelease) {
206       const char *name = nullptr;
207       std::string buf;
208       llvm::raw_string_ostream os(buf);
209 
210       if (requiresRelease) {
211         name = LOpts.getGC() == LangOptions::NonGC
212                ? "missing ivar release (leak)"
213                : "missing ivar release (Hybrid MM, non-GC)";
214 
215         os << "The '" << *ID << "' instance variable in '" << *D
216            << "' was retained by a synthesized property "
217               "but was not released in 'dealloc'";
218       } else {
219         name = LOpts.getGC() == LangOptions::NonGC
220                ? "extra ivar release (use-after-release)"
221                : "extra ivar release (Hybrid MM, non-GC)";
222 
223         os << "The '" << *ID << "' instance variable in '" << *D
224            << "' was not retained by a synthesized property "
225               "but was released in 'dealloc'";
226       }
227 
228       // If @synthesize statement is missing, fall back to @property statement.
229       const Decl *SPDecl = I->getLocation().isValid()
230                                ? static_cast<const Decl *>(I)
231                                : static_cast<const Decl *>(PD);
232       PathDiagnosticLocation SPLoc =
233           PathDiagnosticLocation::createBegin(SPDecl, BR.getSourceManager());
234 
235       BR.EmitBasicReport(MD, Checker, name,
236                          categories::CoreFoundationObjectiveC, os.str(), SPLoc);
237     }
238   }
239 }
240 
241 //===----------------------------------------------------------------------===//
242 // ObjCDeallocChecker
243 //===----------------------------------------------------------------------===//
244 
245 namespace {
246 class ObjCDeallocChecker : public Checker<
247                                       check::ASTDecl<ObjCImplementationDecl> > {
248 public:
249   void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr,
250                     BugReporter &BR) const {
251     if (mgr.getLangOpts().getGC() == LangOptions::GCOnly ||
252         mgr.getLangOpts().ObjCAutoRefCount)
253       return;
254     checkObjCDealloc(this, cast<ObjCImplementationDecl>(D), mgr.getLangOpts(),
255                      BR);
256   }
257 };
258 }
259 
260 void ento::registerObjCDeallocChecker(CheckerManager &mgr) {
261   mgr.registerChecker<ObjCDeallocChecker>();
262 }
263