Coverage Report

Created: 2020-02-25 14:32

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Analysis/ExprMutationAnalyzer.cpp
Line
Count
Source (jump to first uncovered line)
1
//===---------- ExprMutationAnalyzer.cpp ----------------------------------===//
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
#include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
9
#include "clang/ASTMatchers/ASTMatchFinder.h"
10
#include "llvm/ADT/STLExtras.h"
11
12
namespace clang {
13
using namespace ast_matchers;
14
15
namespace {
16
17
5
AST_MATCHER_P(LambdaExpr, hasCaptureInit, const Expr *, E) {
18
5
  return llvm::is_contained(Node.capture_inits(), E);
19
5
}
20
21
AST_MATCHER_P(CXXForRangeStmt, hasRangeStmt,
22
9
              ast_matchers::internal::Matcher<DeclStmt>, InnerMatcher) {
23
9
  const DeclStmt *const Range = Node.getRangeStmt();
24
9
  return InnerMatcher.matches(*Range, Finder, Builder);
25
9
}
26
27
AST_MATCHER_P(Expr, maybeEvalCommaExpr,
28
354
             ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
29
354
  const Expr* Result = &Node;
30
368
  while (const auto *BOComma =
31
14
               dyn_cast_or_null<BinaryOperator>(Result->IgnoreParens())) {
32
14
    if (!BOComma->isCommaOp())
33
0
      break;
34
14
    Result = BOComma->getRHS();
35
14
  }
36
354
  return InnerMatcher.matches(*Result, Finder, Builder);
37
354
}
38
39
const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt, CXXTypeidExpr>
40
    cxxTypeidExpr;
41
42
2
AST_MATCHER(CXXTypeidExpr, isPotentiallyEvaluated) {
43
2
  return Node.isPotentiallyEvaluated();
44
2
}
45
46
const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt,
47
                                                          GenericSelectionExpr>
48
    genericSelectionExpr;
49
50
AST_MATCHER_P(GenericSelectionExpr, hasControllingExpr,
51
1
              ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
52
1
  return InnerMatcher.matches(*Node.getControllingExpr(), Finder, Builder);
53
1
}
54
55
1.13k
const auto nonConstReferenceType = [] {
56
1.13k
  return hasUnqualifiedDesugaredType(
57
1.13k
      referenceType(pointee(unless(isConstQualified()))));
58
1.13k
};
59
60
255
const auto nonConstPointerType = [] {
61
255
  return hasUnqualifiedDesugaredType(
62
255
      pointerType(pointee(unless(isConstQualified()))));
63
255
};
64
65
398
const auto isMoveOnly = [] {
66
398
  return cxxRecordDecl(
67
398
      hasMethod(cxxConstructorDecl(isMoveConstructor(), unless(isDeleted()))),
68
398
      hasMethod(cxxMethodDecl(isMoveAssignmentOperator(), unless(isDeleted()))),
69
398
      unless(anyOf(hasMethod(cxxConstructorDecl(isCopyConstructor(),
70
398
                                                unless(isDeleted()))),
71
398
                   hasMethod(cxxMethodDecl(isCopyAssignmentOperator(),
72
398
                                           unless(isDeleted()))))));
73
398
};
74
75
template <class T> struct NodeID;
76
template <> struct NodeID<Expr> { static const std::string value; };
77
template <> struct NodeID<Decl> { static const std::string value; };
78
const std::string NodeID<Expr>::value = "expr";
79
const std::string NodeID<Decl>::value = "decl";
80
81
template <class T, class F = const Stmt *(ExprMutationAnalyzer::*)(const T *)>
82
const Stmt *tryEachMatch(ArrayRef<ast_matchers::BoundNodes> Matches,
83
1.06k
                         ExprMutationAnalyzer *Analyzer, F Finder) {
84
1.06k
  const StringRef ID = NodeID<T>::value;
85
1.06k
  for (const auto &Nodes : Matches) {
86
66
    if (const Stmt *S = (Analyzer->*Finder)(Nodes.getNodeAs<T>(ID)))
87
31
      return S;
88
66
  }
89
1.06k
  
return nullptr1.03k
;
90
1.06k
}
ExprMutationAnalyzer.cpp:clang::Stmt const* clang::(anonymous namespace)::tryEachMatch<clang::Expr, clang::Stmt const* (clang::ExprMutationAnalyzer::*)(clang::Expr const*)>(llvm::ArrayRef<clang::ast_matchers::BoundNodes>, clang::ExprMutationAnalyzer*, clang::Stmt const* (clang::ExprMutationAnalyzer::*)(clang::Expr const*))
Line
Count
Source
83
777
                         ExprMutationAnalyzer *Analyzer, F Finder) {
84
777
  const StringRef ID = NodeID<T>::value;
85
777
  for (const auto &Nodes : Matches) {
86
44
    if (const Stmt *S = (Analyzer->*Finder)(Nodes.getNodeAs<T>(ID)))
87
20
      return S;
88
44
  }
89
777
  
return nullptr757
;
90
777
}
ExprMutationAnalyzer.cpp:clang::Stmt const* clang::(anonymous namespace)::tryEachMatch<clang::Decl, clang::Stmt const* (clang::ExprMutationAnalyzer::*)(clang::Decl const*)>(llvm::ArrayRef<clang::ast_matchers::BoundNodes>, clang::ExprMutationAnalyzer*, clang::Stmt const* (clang::ExprMutationAnalyzer::*)(clang::Decl const*))
Line
Count
Source
83
288
                         ExprMutationAnalyzer *Analyzer, F Finder) {
84
288
  const StringRef ID = NodeID<T>::value;
85
288
  for (const auto &Nodes : Matches) {
86
22
    if (const Stmt *S = (Analyzer->*Finder)(Nodes.getNodeAs<T>(ID)))
87
11
      return S;
88
22
  }
89
288
  
return nullptr277
;
90
288
}
91
92
} // namespace
93
94
274
const Stmt *ExprMutationAnalyzer::findMutation(const Expr *Exp) {
95
274
  return findMutationMemoized(Exp,
96
274
                              {&ExprMutationAnalyzer::findDirectMutation,
97
274
                               &ExprMutationAnalyzer::findMemberMutation,
98
274
                               &ExprMutationAnalyzer::findArrayElementMutation,
99
274
                               &ExprMutationAnalyzer::findCastMutation,
100
274
                               &ExprMutationAnalyzer::findRangeLoopMutation,
101
274
                               &ExprMutationAnalyzer::findReferenceMutation,
102
274
                               &ExprMutationAnalyzer::findFunctionArgMutation},
103
274
                              Results);
104
274
}
105
106
48
const Stmt *ExprMutationAnalyzer::findMutation(const Decl *Dec) {
107
48
  return tryEachDeclRef(Dec, &ExprMutationAnalyzer::findMutation);
108
48
}
109
110
0
const Stmt *ExprMutationAnalyzer::findPointeeMutation(const Expr *Exp) {
111
0
  return findMutationMemoized(Exp, {/*TODO*/}, PointeeResults);
112
0
}
113
114
0
const Stmt *ExprMutationAnalyzer::findPointeeMutation(const Decl *Dec) {
115
0
  return tryEachDeclRef(Dec, &ExprMutationAnalyzer::findPointeeMutation);
116
0
}
117
118
const Stmt *ExprMutationAnalyzer::findMutationMemoized(
119
    const Expr *Exp, llvm::ArrayRef<MutationFinder> Finders,
120
274
    ResultMap &MemoizedResults) {
121
274
  const auto Memoized = MemoizedResults.find(Exp);
122
274
  if (Memoized != MemoizedResults.end())
123
11
    return Memoized->second;
124
263
125
263
  if (isUnevaluated(Exp))
126
8
    return MemoizedResults[Exp] = nullptr;
127
255
128
1.15k
  
for (const auto &Finder : Finders)255
{
129
1.15k
    if (const Stmt *S = (this->*Finder)(Exp))
130
133
      return MemoizedResults[Exp] = S;
131
1.15k
  }
132
255
133
255
  
return MemoizedResults[Exp] = nullptr122
;
134
255
}
135
136
const Stmt *ExprMutationAnalyzer::tryEachDeclRef(const Decl *Dec,
137
48
                                                 MutationFinder Finder) {
138
48
  const auto Refs =
139
48
      match(findAll(declRefExpr(to(equalsNode(Dec))).bind(NodeID<Expr>::value)),
140
48
            Stm, Context);
141
48
  for (const auto &RefNodes : Refs) {
142
39
    const auto *E = RefNodes.getNodeAs<Expr>(NodeID<Expr>::value);
143
39
    if ((this->*Finder)(E))
144
21
      return E;
145
39
  }
146
48
  
return nullptr27
;
147
48
}
148
149
263
bool ExprMutationAnalyzer::isUnevaluated(const Expr *Exp) {
150
263
  return selectFirst<Expr>(
151
263
             NodeID<Expr>::value,
152
263
             match(
153
263
                 findAll(
154
263
                     expr(equalsNode(Exp),
155
263
                          anyOf(
156
263
                              // `Exp` is part of the underlying expression of
157
263
                              // decltype/typeof if it has an ancestor of
158
263
                              // typeLoc.
159
263
                              hasAncestor(typeLoc(unless(
160
263
                                  hasAncestor(unaryExprOrTypeTraitExpr())))),
161
263
                              hasAncestor(expr(anyOf(
162
263
                                  // `UnaryExprOrTypeTraitExpr` is unevaluated
163
263
                                  // unless it's sizeof on VLA.
164
263
                                  unaryExprOrTypeTraitExpr(unless(sizeOfExpr(
165
263
                                      hasArgumentOfType(variableArrayType())))),
166
263
                                  // `CXXTypeidExpr` is unevaluated unless it's
167
263
                                  // applied to an expression of glvalue of
168
263
                                  // polymorphic class type.
169
263
                                  cxxTypeidExpr(
170
263
                                      unless(isPotentiallyEvaluated())),
171
263
                                  // The controlling expression of
172
263
                                  // `GenericSelectionExpr` is unevaluated.
173
263
                                  genericSelectionExpr(hasControllingExpr(
174
263
                                      hasDescendant(equalsNode(Exp)))),
175
263
                                  cxxNoexceptExpr())))))
176
263
                         .bind(NodeID<Expr>::value)),
177
263
                 Stm, Context)) != nullptr;
178
263
}
179
180
const Stmt *
181
777
ExprMutationAnalyzer::findExprMutation(ArrayRef<BoundNodes> Matches) {
182
777
  return tryEachMatch<Expr>(Matches, this, &ExprMutationAnalyzer::findMutation);
183
777
}
184
185
const Stmt *
186
288
ExprMutationAnalyzer::findDeclMutation(ArrayRef<BoundNodes> Matches) {
187
288
  return tryEachMatch<Decl>(Matches, this, &ExprMutationAnalyzer::findMutation);
188
288
}
189
190
const Stmt *ExprMutationAnalyzer::findExprPointeeMutation(
191
0
    ArrayRef<ast_matchers::BoundNodes> Matches) {
192
0
  return tryEachMatch<Expr>(Matches, this,
193
0
                            &ExprMutationAnalyzer::findPointeeMutation);
194
0
}
195
196
const Stmt *ExprMutationAnalyzer::findDeclPointeeMutation(
197
0
    ArrayRef<ast_matchers::BoundNodes> Matches) {
198
0
  return tryEachMatch<Decl>(Matches, this,
199
0
                            &ExprMutationAnalyzer::findPointeeMutation);
200
0
}
201
202
255
const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) {
203
255
  // LHS of any assignment operators.
204
255
  const auto AsAssignmentLhs =
205
255
      binaryOperator(isAssignmentOperator(),
206
255
                     hasLHS(maybeEvalCommaExpr(equalsNode(Exp))));
207
255
208
255
  // Operand of increment/decrement operators.
209
255
  const auto AsIncDecOperand =
210
255
      unaryOperator(anyOf(hasOperatorName("++"), hasOperatorName("--")),
211
255
                    hasUnaryOperand(maybeEvalCommaExpr(equalsNode(Exp))));
212
255
213
255
  // Invoking non-const member function.
214
255
  // A member function is assumed to be non-const when it is unresolved.
215
255
  const auto NonConstMethod = cxxMethodDecl(unless(isConst()));
216
255
  const auto AsNonConstThis =
217
255
      expr(anyOf(cxxMemberCallExpr(callee(NonConstMethod),
218
255
                                   on(maybeEvalCommaExpr(equalsNode(Exp)))),
219
255
                 cxxOperatorCallExpr(callee(NonConstMethod),
220
255
                                     hasArgument(0,
221
255
                                                 maybeEvalCommaExpr(equalsNode(Exp)))),
222
255
                 callExpr(callee(expr(anyOf(
223
255
                     unresolvedMemberExpr(
224
255
                       hasObjectExpression(maybeEvalCommaExpr(equalsNode(Exp)))),
225
255
                     cxxDependentScopeMemberExpr(
226
255
                         hasObjectExpression(maybeEvalCommaExpr(equalsNode(Exp))))))))));
227
255
228
255
  // Taking address of 'Exp'.
229
255
  // We're assuming 'Exp' is mutated as soon as its address is taken, though in
230
255
  // theory we can follow the pointer and see whether it escaped `Stm` or is
231
255
  // dereferenced and then mutated. This is left for future improvements.
232
255
  const auto AsAmpersandOperand =
233
255
      unaryOperator(hasOperatorName("&"),
234
255
                    // A NoOp implicit cast is adding const.
235
255
                    unless(hasParent(implicitCastExpr(hasCastKind(CK_NoOp)))),
236
255
                    hasUnaryOperand(maybeEvalCommaExpr(equalsNode(Exp))));
237
255
  const auto AsPointerFromArrayDecay =
238
255
      castExpr(hasCastKind(CK_ArrayToPointerDecay),
239
255
               unless(hasParent(arraySubscriptExpr())),
240
255
               has(maybeEvalCommaExpr(equalsNode(Exp))));
241
255
  // Treat calling `operator->()` of move-only classes as taking address.
242
255
  // These are typically smart pointers with unique ownership so we treat
243
255
  // mutation of pointee as mutation of the smart pointer itself.
244
255
  const auto AsOperatorArrowThis =
245
255
      cxxOperatorCallExpr(hasOverloadedOperatorName("->"),
246
255
                          callee(cxxMethodDecl(ofClass(isMoveOnly()),
247
255
                                               returns(nonConstPointerType()))),
248
255
                          argumentCountIs(1),
249
255
                          hasArgument(0, maybeEvalCommaExpr(equalsNode(Exp))));
250
255
251
255
  // Used as non-const-ref argument when calling a function.
252
255
  // An argument is assumed to be non-const-ref when the function is unresolved.
253
255
  // Instantiated template functions are not handled here but in
254
255
  // findFunctionArgMutation which has additional smarts for handling forwarding
255
255
  // references.
256
255
  const auto NonConstRefParam = forEachArgumentWithParam(
257
255
      maybeEvalCommaExpr(equalsNode(Exp)),
258
255
      parmVarDecl(hasType(nonConstReferenceType())));
259
255
  const auto NotInstantiated = unless(hasDeclaration(isInstantiated()));
260
255
  const auto AsNonConstRefArg = anyOf(
261
255
      callExpr(NonConstRefParam, NotInstantiated),
262
255
      cxxConstructExpr(NonConstRefParam, NotInstantiated),
263
255
      callExpr(callee(expr(anyOf(unresolvedLookupExpr(), unresolvedMemberExpr(),
264
255
                                 cxxDependentScopeMemberExpr(),
265
255
                                 hasType(templateTypeParmType())))),
266
255
               hasAnyArgument(maybeEvalCommaExpr(equalsNode(Exp)))),
267
255
      cxxUnresolvedConstructExpr(hasAnyArgument(maybeEvalCommaExpr(equalsNode(Exp)))));
268
255
269
255
  // Captured by a lambda by reference.
270
255
  // If we're initializing a capture with 'Exp' directly then we're initializing
271
255
  // a reference capture.
272
255
  // For value captures there will be an ImplicitCastExpr <LValueToRValue>.
273
255
  const auto AsLambdaRefCaptureInit = lambdaExpr(hasCaptureInit(Exp));
274
255
275
255
  // Returned as non-const-ref.
276
255
  // If we're returning 'Exp' directly then it's returned as non-const-ref.
277
255
  // For returning by value there will be an ImplicitCastExpr <LValueToRValue>.
278
255
  // For returning by const-ref there will be an ImplicitCastExpr <NoOp> (for
279
255
  // adding const.)
280
255
  const auto AsNonConstRefReturn = returnStmt(hasReturnValue(
281
255
                                                maybeEvalCommaExpr(equalsNode(Exp))));
282
255
283
255
  const auto Matches =
284
255
      match(findAll(stmt(anyOf(AsAssignmentLhs, AsIncDecOperand, AsNonConstThis,
285
255
                               AsAmpersandOperand, AsPointerFromArrayDecay,
286
255
                               AsOperatorArrowThis, AsNonConstRefArg,
287
255
                               AsLambdaRefCaptureInit, AsNonConstRefReturn))
288
255
                        .bind("stmt")),
289
255
            Stm, Context);
290
255
  return selectFirst<Stmt>("stmt", Matches);
291
255
}
292
293
165
const Stmt *ExprMutationAnalyzer::findMemberMutation(const Expr *Exp) {
294
165
  // Check whether any member of 'Exp' is mutated.
295
165
  const auto MemberExprs =
296
165
      match(findAll(expr(anyOf(memberExpr(hasObjectExpression(equalsNode(Exp))),
297
165
                               cxxDependentScopeMemberExpr(
298
165
                                   hasObjectExpression(equalsNode(Exp)))))
299
165
                        .bind(NodeID<Expr>::value)),
300
165
            Stm, Context);
301
165
  return findExprMutation(MemberExprs);
302
165
}
303
304
158
const Stmt *ExprMutationAnalyzer::findArrayElementMutation(const Expr *Exp) {
305
158
  // Check whether any element of an array is mutated.
306
158
  const auto SubscriptExprs = match(
307
158
      findAll(arraySubscriptExpr(hasBase(ignoringImpCasts(equalsNode(Exp))))
308
158
                  .bind(NodeID<Expr>::value)),
309
158
      Stm, Context);
310
158
  return findExprMutation(SubscriptExprs);
311
158
}
312
313
157
const Stmt *ExprMutationAnalyzer::findCastMutation(const Expr *Exp) {
314
157
  // If 'Exp' is casted to any non-const reference type, check the castExpr.
315
157
  const auto Casts =
316
157
      match(findAll(castExpr(hasSourceExpression(equalsNode(Exp)),
317
157
                             anyOf(explicitCastExpr(hasDestinationType(
318
157
                                       nonConstReferenceType())),
319
157
                                   implicitCastExpr(hasImplicitDestinationType(
320
157
                                       nonConstReferenceType()))))
321
157
                        .bind(NodeID<Expr>::value)),
322
157
            Stm, Context);
323
157
  if (const Stmt *S = findExprMutation(Casts))
324
3
    return S;
325
154
  // Treat std::{move,forward} as cast.
326
154
  const auto Calls =
327
154
      match(findAll(callExpr(callee(namedDecl(
328
154
                                 hasAnyName("::std::move", "::std::forward"))),
329
154
                             hasArgument(0, equalsNode(Exp)))
330
154
                        .bind("expr")),
331
154
            Stm, Context);
332
154
  return findExprMutation(Calls);
333
154
}
334
335
146
const Stmt *ExprMutationAnalyzer::findRangeLoopMutation(const Expr *Exp) {
336
146
  // If range for looping over 'Exp' with a non-const reference loop variable,
337
146
  // check all declRefExpr of the loop variable.
338
146
  const auto LoopVars =
339
146
      match(findAll(cxxForRangeStmt(
340
146
                hasLoopVariable(varDecl(hasType(nonConstReferenceType()))
341
146
                                    .bind(NodeID<Decl>::value)),
342
146
                hasRangeInit(equalsNode(Exp)))),
343
146
            Stm, Context);
344
146
  return findDeclMutation(LoopVars);
345
146
}
346
347
143
const Stmt *ExprMutationAnalyzer::findReferenceMutation(const Expr *Exp) {
348
143
  // Follow non-const reference returned by `operator*()` of move-only classes.
349
143
  // These are typically smart pointers with unique ownership so we treat
350
143
  // mutation of pointee as mutation of the smart pointer itself.
351
143
  const auto Ref =
352
143
      match(findAll(cxxOperatorCallExpr(
353
143
                        hasOverloadedOperatorName("*"),
354
143
                        callee(cxxMethodDecl(ofClass(isMoveOnly()),
355
143
                                             returns(nonConstReferenceType()))),
356
143
                        argumentCountIs(1), hasArgument(0, equalsNode(Exp)))
357
143
                        .bind(NodeID<Expr>::value)),
358
143
            Stm, Context);
359
143
  if (const Stmt *S = findExprMutation(Ref))
360
1
    return S;
361
142
362
142
  // If 'Exp' is bound to a non-const reference, check all declRefExpr to that.
363
142
  const auto Refs = match(
364
142
      stmt(forEachDescendant(
365
142
          varDecl(
366
142
              hasType(nonConstReferenceType()),
367
142
              hasInitializer(anyOf(equalsNode(Exp),
368
142
                                   conditionalOperator(anyOf(
369
142
                                       hasTrueExpression(equalsNode(Exp)),
370
142
                                       hasFalseExpression(equalsNode(Exp)))))),
371
142
              hasParent(declStmt().bind("stmt")),
372
142
              // Don't follow the reference in range statement, we've handled
373
142
              // that separately.
374
142
              unless(hasParent(declStmt(hasParent(
375
142
                  cxxForRangeStmt(hasRangeStmt(equalsBoundNode("stmt"))))))))
376
142
              .bind(NodeID<Decl>::value))),
377
142
      Stm, Context);
378
142
  return findDeclMutation(Refs);
379
142
}
380
381
134
const Stmt *ExprMutationAnalyzer::findFunctionArgMutation(const Expr *Exp) {
382
134
  const auto NonConstRefParam = forEachArgumentWithParam(
383
134
      equalsNode(Exp),
384
134
      parmVarDecl(hasType(nonConstReferenceType())).bind("parm"));
385
134
  const auto IsInstantiated = hasDeclaration(isInstantiated());
386
134
  const auto FuncDecl = hasDeclaration(functionDecl().bind("func"));
387
134
  const auto Matches = match(
388
134
      findAll(expr(anyOf(callExpr(NonConstRefParam, IsInstantiated, FuncDecl,
389
134
                                  unless(callee(namedDecl(hasAnyName(
390
134
                                      "::std::move", "::std::forward"))))),
391
134
                         cxxConstructExpr(NonConstRefParam, IsInstantiated,
392
134
                                          FuncDecl)))
393
134
                  .bind(NodeID<Expr>::value)),
394
134
      Stm, Context);
395
134
  for (const auto &Nodes : Matches) {
396
25
    const auto *Exp = Nodes.getNodeAs<Expr>(NodeID<Expr>::value);
397
25
    const auto *Func = Nodes.getNodeAs<FunctionDecl>("func");
398
25
    if (!Func->getBody() || 
!Func->getPrimaryTemplate()23
)
399
3
      return Exp;
400
22
401
22
    const auto *Parm = Nodes.getNodeAs<ParmVarDecl>("parm");
402
22
    const ArrayRef<ParmVarDecl *> AllParams =
403
22
        Func->getPrimaryTemplate()->getTemplatedDecl()->parameters();
404
22
    QualType ParmType =
405
22
        AllParams[std::min<size_t>(Parm->getFunctionScopeIndex(),
406
22
                                   AllParams.size() - 1)]
407
22
            ->getType();
408
22
    if (const auto *T = ParmType->getAs<PackExpansionType>())
409
12
      ParmType = T->getPattern();
410
22
411
22
    // If param type is forwarding reference, follow into the function
412
22
    // definition and see whether the param is mutated inside.
413
22
    if (const auto *RefType = ParmType->getAs<RValueReferenceType>()) {
414
22
      if (!RefType->getPointeeType().getQualifiers() &&
415
22
          RefType->getPointeeType()->getAs<TemplateTypeParmType>()) {
416
22
        std::unique_ptr<FunctionParmMutationAnalyzer> &Analyzer =
417
22
            FuncParmAnalyzer[Func];
418
22
        if (!Analyzer)
419
22
          Analyzer.reset(new FunctionParmMutationAnalyzer(*Func, Context));
420
22
        if (Analyzer->findMutation(Parm))
421
9
          return Exp;
422
13
        continue;
423
13
      }
424
22
    }
425
0
    // Not forwarding reference.
426
0
    return Exp;
427
0
  }
428
134
  
return nullptr122
;
429
134
}
430
431
FunctionParmMutationAnalyzer::FunctionParmMutationAnalyzer(
432
    const FunctionDecl &Func, ASTContext &Context)
433
22
    : BodyAnalyzer(*Func.getBody(), Context) {
434
22
  if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(&Func)) {
435
7
    // CXXCtorInitializer might also mutate Param but they're not part of
436
7
    // function body, check them eagerly here since they're typically trivial.
437
7
    for (const CXXCtorInitializer *Init : Ctor->inits()) {
438
5
      ExprMutationAnalyzer InitAnalyzer(*Init->getInit(), Context);
439
6
      for (const ParmVarDecl *Parm : Ctor->parameters()) {
440
6
        if (Results.find(Parm) != Results.end())
441
0
          continue;
442
6
        if (const Stmt *S = InitAnalyzer.findMutation(Parm))
443
3
          Results[Parm] = S;
444
6
      }
445
5
    }
446
7
  }
447
22
}
448
449
const Stmt *
450
22
FunctionParmMutationAnalyzer::findMutation(const ParmVarDecl *Parm) {
451
22
  const auto Memoized = Results.find(Parm);
452
22
  if (Memoized != Results.end())
453
2
    return Memoized->second;
454
20
455
20
  if (const Stmt *S = BodyAnalyzer.findMutation(Parm))
456
7
    return Results[Parm] = S;
457
13
458
13
  return Results[Parm] = nullptr;
459
13
}
460
461
} // namespace clang