Coverage Report

Created: 2020-09-15 12:33

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