Coverage Report

Created: 2019-07-24 05:18

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