clang  10.0.0git
RunLoopAutoreleaseLeakChecker.cpp
Go to the documentation of this file.
1 //=- RunLoopAutoreleaseLeakChecker.cpp --------------------------*- C++ -*-==//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // A checker for detecting leaks resulting from allocating temporary
11 // autoreleased objects before starting the main run loop.
12 //
13 // Checks for two antipatterns:
14 // 1. ObjCMessageExpr followed by [[NSRunLoop mainRunLoop] run] in the same
15 // autorelease pool.
16 // 2. ObjCMessageExpr followed by [[NSRunLoop mainRunLoop] run] in no
17 // autorelease pool.
18 //
19 // Any temporary objects autoreleased in code called in those expressions
20 // will not be deallocated until the program exits, and are effectively leaks.
21 //
22 //===----------------------------------------------------------------------===//
23 //
24 
26 #include "clang/AST/Decl.h"
27 #include "clang/AST/DeclObjC.h"
36 
37 using namespace clang;
38 using namespace ento;
39 using namespace ast_matchers;
40 
41 namespace {
42 
43 const char * RunLoopBind = "NSRunLoopM";
44 const char * RunLoopRunBind = "RunLoopRunM";
45 const char * OtherMsgBind = "OtherMessageSentM";
46 const char * AutoreleasePoolBind = "AutoreleasePoolM";
47 const char * OtherStmtAutoreleasePoolBind = "OtherAutoreleasePoolM";
48 
49 class RunLoopAutoreleaseLeakChecker : public Checker<check::ASTCodeBody> {
50 
51 public:
52  void checkASTCodeBody(const Decl *D,
53  AnalysisManager &AM,
54  BugReporter &BR) const;
55 
56 };
57 
58 } // end anonymous namespace
59 
60 /// \return Whether {@code A} occurs before {@code B} in traversal of
61 /// {@code Parent}.
62 /// Conceptually a very incomplete/unsound approximation of happens-before
63 /// relationship (A is likely to be evaluated before B),
64 /// but useful enough in this case.
65 static bool seenBefore(const Stmt *Parent, const Stmt *A, const Stmt *B) {
66  for (const Stmt *C : Parent->children()) {
67  if (!C) continue;
68 
69  if (C == A)
70  return true;
71 
72  if (C == B)
73  return false;
74 
75  return seenBefore(C, A, B);
76  }
77  return false;
78 }
79 
80 static void emitDiagnostics(BoundNodes &Match,
81  const Decl *D,
82  BugReporter &BR,
83  AnalysisManager &AM,
84  const RunLoopAutoreleaseLeakChecker *Checker) {
85 
86  assert(D->hasBody());
87  const Stmt *DeclBody = D->getBody();
88 
89  AnalysisDeclContext *ADC = AM.getAnalysisDeclContext(D);
90 
91  const auto *ME = Match.getNodeAs<ObjCMessageExpr>(OtherMsgBind);
92  assert(ME);
93 
94  const auto *AP =
95  Match.getNodeAs<ObjCAutoreleasePoolStmt>(AutoreleasePoolBind);
96  const auto *OAP =
97  Match.getNodeAs<ObjCAutoreleasePoolStmt>(OtherStmtAutoreleasePoolBind);
98  bool HasAutoreleasePool = (AP != nullptr);
99 
100  const auto *RL = Match.getNodeAs<ObjCMessageExpr>(RunLoopBind);
101  const auto *RLR = Match.getNodeAs<Stmt>(RunLoopRunBind);
102  assert(RLR && "Run loop launch not found");
103  assert(ME != RLR);
104 
105  // Launch of run loop occurs before the message-sent expression is seen.
106  if (seenBefore(DeclBody, RLR, ME))
107  return;
108 
109  if (HasAutoreleasePool && (OAP != AP))
110  return;
111 
112  PathDiagnosticLocation Location = PathDiagnosticLocation::createBegin(
113  ME, BR.getSourceManager(), ADC);
114  SourceRange Range = ME->getSourceRange();
115 
116  BR.EmitBasicReport(ADC->getDecl(), Checker,
117  /*Name=*/"Memory leak inside autorelease pool",
118  /*BugCategory=*/"Memory",
119  /*Name=*/
120  (Twine("Temporary objects allocated in the") +
121  " autorelease pool " +
122  (HasAutoreleasePool ? "" : "of last resort ") +
123  "followed by the launch of " +
124  (RL ? "main run loop " : "xpc_main ") +
125  "may never get released; consider moving them to a "
126  "separate autorelease pool")
127  .str(),
128  Location, Range);
129 }
130 
131 static StatementMatcher getRunLoopRunM(StatementMatcher Extra = anything()) {
132  StatementMatcher MainRunLoopM =
133  objcMessageExpr(hasSelector("mainRunLoop"),
134  hasReceiverType(asString("NSRunLoop")),
135  Extra)
136  .bind(RunLoopBind);
137 
138  StatementMatcher MainRunLoopRunM = objcMessageExpr(hasSelector("run"),
139  hasReceiver(MainRunLoopM),
140  Extra).bind(RunLoopRunBind);
141 
142  StatementMatcher XPCRunM =
143  callExpr(callee(functionDecl(hasName("xpc_main")))).bind(RunLoopRunBind);
144  return anyOf(MainRunLoopRunM, XPCRunM);
145 }
146 
147 static StatementMatcher getOtherMessageSentM(StatementMatcher Extra = anything()) {
148  return objcMessageExpr(unless(anyOf(equalsBoundNode(RunLoopBind),
149  equalsBoundNode(RunLoopRunBind))),
150  Extra)
151  .bind(OtherMsgBind);
152 }
153 
154 static void
155 checkTempObjectsInSamePool(const Decl *D, AnalysisManager &AM, BugReporter &BR,
156  const RunLoopAutoreleaseLeakChecker *Chkr) {
157  StatementMatcher RunLoopRunM = getRunLoopRunM();
158  StatementMatcher OtherMessageSentM = getOtherMessageSentM(
159  hasAncestor(autoreleasePoolStmt().bind(OtherStmtAutoreleasePoolBind)));
160 
161  StatementMatcher RunLoopInAutorelease =
163  hasDescendant(RunLoopRunM),
164  hasDescendant(OtherMessageSentM)).bind(AutoreleasePoolBind);
165 
166  DeclarationMatcher GroupM = decl(hasDescendant(RunLoopInAutorelease));
167 
168  auto Matches = match(GroupM, *D, AM.getASTContext());
169  for (BoundNodes Match : Matches)
170  emitDiagnostics(Match, D, BR, AM, Chkr);
171 }
172 
173 static void
174 checkTempObjectsInNoPool(const Decl *D, AnalysisManager &AM, BugReporter &BR,
175  const RunLoopAutoreleaseLeakChecker *Chkr) {
176 
177  auto NoPoolM = unless(hasAncestor(autoreleasePoolStmt()));
178 
179  StatementMatcher RunLoopRunM = getRunLoopRunM(NoPoolM);
180  StatementMatcher OtherMessageSentM = getOtherMessageSentM(NoPoolM);
181 
183  isMain(),
184  hasDescendant(RunLoopRunM),
185  hasDescendant(OtherMessageSentM)
186  );
187 
188  auto Matches = match(GroupM, *D, AM.getASTContext());
189 
190  for (BoundNodes Match : Matches)
191  emitDiagnostics(Match, D, BR, AM, Chkr);
192 
193 }
194 
195 void RunLoopAutoreleaseLeakChecker::checkASTCodeBody(const Decl *D,
196  AnalysisManager &AM,
197  BugReporter &BR) const {
198  checkTempObjectsInSamePool(D, AM, BR, this);
199  checkTempObjectsInNoPool(D, AM, BR, this);
200 }
201 
202 void ento::registerRunLoopAutoreleaseLeakChecker(CheckerManager &mgr) {
203  mgr.registerChecker<RunLoopAutoreleaseLeakChecker>();
204 }
205 
206 bool ento::shouldRegisterRunLoopAutoreleaseLeakChecker(const LangOptions &LO) {
207  return true;
208 }
internal::TrueMatcher anything()
Matches any node.
Definition: ASTMatchers.h:161
const internal::VariadicDynCastAllOfMatcher< Stmt, CallExpr > callExpr
Matches call expressions.
virtual Stmt * getBody() const
getBody - If this Decl represents a declaration for a body of code, such as a function or method defi...
Definition: DeclBase.h:986
Stmt - This represents one statement.
Definition: Stmt.h:66
internal::Matcher< Stmt > StatementMatcher
Definition: ASTMatchers.h:139
const internal::VariadicOperatorMatcherFunc< 2, std::numeric_limits< unsigned >::max()> anyOf
Matches if any of the given matchers matches.
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:88
const internal::ArgumentAdaptingMatcherFunc< internal::HasDescendantMatcher > hasDescendant
Matches AST nodes that have descendant AST nodes that match the provided matcher. ...
const internal::VariadicDynCastAllOfMatcher< Decl, FunctionDecl > functionDecl
Matches function declarations.
AnalysisDeclContext contains the context data for the function or method under analysis.
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:53
const T * getNodeAs(StringRef ID) const
Returns the AST node bound to ID.
Definition: ASTMatchers.h:111
child_range children()
Definition: Stmt.cpp:224
SmallVector< BoundNodes, 1 > match(MatcherT Matcher, const NodeT &Node, ASTContext &Context)
Returns the results of matching Matcher on Node.
NodeId Parent
Definition: ASTDiff.cpp:191
const internal::VariadicDynCastAllOfMatcher< Stmt, ObjCAutoreleasePoolStmt > autoreleasePoolStmt
Matches an Objective-C autorelease pool statement.
const internal::VariadicAllOfMatcher< Decl > decl
Matches declarations.
An expression that sends a message to the given Objective-C object or class.
Definition: ExprObjC.h:950
const internal::ArgumentAdaptingMatcherFunc< internal::HasAncestorMatcher, internal::TypeList< Decl, NestedNameSpecifierLoc, Stmt, TypeLoc >, internal::TypeList< Decl, NestedNameSpecifierLoc, Stmt, TypeLoc > > hasAncestor
Matches AST nodes that have an ancestor that matches the provided matcher.
virtual bool hasBody() const
Returns true if this Decl represents a declaration for a body of code, such as a function or method d...
Definition: DeclBase.h:992
Maps string IDs to AST nodes matched by parts of a matcher.
Definition: ASTMatchers.h:104
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
ASTContext & getASTContext() const LLVM_READONLY
Definition: DeclBase.cpp:377
const Decl * getDecl() const
static void emitDiagnostics(BoundNodes &Match, const Decl *D, BugReporter &BR, AnalysisManager &AM, const ObjCAutoreleaseWriteChecker *Checker)
const internal::VariadicOperatorMatcherFunc< 1, 1 > unless
Matches if the provided matcher does not match.
Dataflow Directional Tag Classes.
internal::Matcher< NamedDecl > hasName(const std::string &Name)
Matches NamedDecl nodes that have the specified name.
Definition: ASTMatchers.h:2649
A trivial tuple used to represent a source range.
const internal::VariadicDynCastAllOfMatcher< Stmt, ObjCMessageExpr > objcMessageExpr
Matches ObjectiveC Message invocation expressions.
Represents Objective-C&#39;s @autoreleasepool Statement.
Definition: StmtObjC.h:368
internal::Matcher< Decl > DeclarationMatcher
Types of matchers for the top-level classes in the AST class hierarchy.
Definition: ASTMatchers.h:138