xref: /llvm-project/clang-tools-extra/clang-tidy/modernize/AvoidBindCheck.cpp (revision e40a742a5008c5a4cf647c0ea805486498786393)
1 //===--- AvoidBindCheck.cpp - clang-tidy-----------------------------------===//
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 #include "AvoidBindCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/Basic/LLVM.h"
13 #include "clang/Basic/LangOptions.h"
14 #include "clang/Basic/SourceLocation.h"
15 #include "clang/Lex/Lexer.h"
16 #include "llvm/ADT/ArrayRef.h"
17 #include "llvm/ADT/STLExtras.h"
18 #include "llvm/ADT/SmallSet.h"
19 #include "llvm/ADT/SmallVector.h"
20 #include "llvm/ADT/StringRef.h"
21 #include "llvm/Support/Casting.h"
22 #include "llvm/Support/FormatVariadic.h"
23 #include "llvm/Support/Regex.h"
24 #include "llvm/Support/raw_ostream.h"
25 #include <algorithm>
26 #include <cstddef>
27 #include <string>
28 
29 using namespace clang::ast_matchers;
30 
31 namespace clang {
32 namespace tidy {
33 namespace modernize {
34 
35 namespace {
36 
37 enum BindArgumentKind { BK_Temporary, BK_Placeholder, BK_CallExpr, BK_Other };
38 enum CaptureMode { CM_None, CM_ByRef, CM_ByValue, CM_InitExpression };
39 
40 enum CallableType {
41   CT_Other,          // unknown
42   CT_Function,       // global or static function
43   CT_MemberFunction, // member function with implicit this
44   CT_Object,         // object with operator()
45 };
46 
47 enum CallableMaterializationKind {
48   CMK_Other,       // unknown
49   CMK_Function,    // callable is the name of a member or non-member function.
50   CMK_VariableRef, // callable is a simple expression involving a global or
51                    // local variable.
52   CMK_CallExpression, // callable is obtained as the result of a call expression
53 };
54 
55 struct BindArgument {
56   // A rough classification of the type of expression this argument was.
57   BindArgumentKind Kind = BK_Other;
58 
59   // If this argument required a capture, a value indicating how it was
60   // captured.
61   CaptureMode CM = CM_None;
62 
63   // The exact spelling of this argument in the source code.
64   StringRef SourceTokens;
65 
66   // The identifier of the variable within the capture list.  This may be
67   // different from UsageIdentifier for example in the expression *d, where the
68   // variable is captured as d, but referred to as *d.
69   std::string CaptureIdentifier;
70 
71   // If this is a placeholder or capture init expression, contains the tokens
72   // used to refer to this parameter from within the body of the lambda.
73   std::string UsageIdentifier;
74 
75   // If Kind == BK_Placeholder, the index of the placeholder.
76   size_t PlaceHolderIndex = 0;
77 
78   // True if the argument is used inside the lambda, false otherwise.
79   bool IsUsed = false;
80 
81   // The actual Expr object representing this expression.
82   const Expr *E = nullptr;
83 };
84 
85 struct CallableInfo {
86   CallableType Type = CT_Other;
87   CallableMaterializationKind Materialization = CMK_Other;
88   CaptureMode CM = CM_None;
89   StringRef SourceTokens;
90   std::string CaptureIdentifier;
91   std::string UsageIdentifier;
92   StringRef CaptureInitializer;
93   const FunctionDecl *Decl = nullptr;
94 };
95 
96 struct LambdaProperties {
97   CallableInfo Callable;
98   SmallVector<BindArgument, 4> BindArguments;
99   StringRef BindNamespace;
100   bool IsFixitSupported = false;
101 };
102 
103 } // end namespace
104 
105 static const Expr *ignoreTemporariesAndPointers(const Expr *E) {
106   if (const auto *T = dyn_cast<UnaryOperator>(E))
107     return ignoreTemporariesAndPointers(T->getSubExpr());
108 
109   const Expr *F = E->IgnoreImplicit();
110   if (E != F)
111     return ignoreTemporariesAndPointers(F);
112 
113   return E;
114 }
115 
116 static const Expr *ignoreTemporariesAndConstructors(const Expr *E) {
117   if (const auto *T = dyn_cast<CXXConstructExpr>(E))
118     return ignoreTemporariesAndConstructors(T->getArg(0));
119 
120   const Expr *F = E->IgnoreImplicit();
121   if (E != F)
122     return ignoreTemporariesAndPointers(F);
123 
124   return E;
125 }
126 
127 static StringRef getSourceTextForExpr(const MatchFinder::MatchResult &Result,
128                                       const Expr *E) {
129   return Lexer::getSourceText(
130       CharSourceRange::getTokenRange(E->getBeginLoc(), E->getEndLoc()),
131       *Result.SourceManager, Result.Context->getLangOpts());
132 }
133 
134 static bool isCallExprNamed(const Expr *E, StringRef Name) {
135   const auto *CE = dyn_cast<CallExpr>(E->IgnoreImplicit());
136   if (!CE)
137     return false;
138   const auto *ND = dyn_cast<NamedDecl>(CE->getCalleeDecl());
139   if (!ND)
140     return false;
141   return ND->getQualifiedNameAsString() == Name;
142 }
143 
144 static void
145 initializeBindArgumentForCallExpr(const MatchFinder::MatchResult &Result,
146                                   BindArgument &B, const CallExpr *CE,
147                                   unsigned &CaptureIndex) {
148   // std::ref(x) means to capture x by reference.
149   if (isCallExprNamed(CE, "boost::ref") || isCallExprNamed(CE, "std::ref")) {
150     B.Kind = BK_Other;
151     B.CM = CM_ByRef;
152     B.UsageIdentifier =
153         std::string(getSourceTextForExpr(Result, CE->getArg(0)));
154   } else {
155     B.Kind = BK_CallExpr;
156     B.CM = CM_InitExpression;
157     B.UsageIdentifier = "capture" + llvm::utostr(CaptureIndex++);
158   }
159   B.CaptureIdentifier = B.UsageIdentifier;
160 }
161 
162 static bool anyDescendantIsLocal(const Stmt *Statement) {
163   if (const auto *DeclRef = dyn_cast<DeclRefExpr>(Statement)) {
164     const ValueDecl *Decl = DeclRef->getDecl();
165     if (const auto *Var = dyn_cast_or_null<VarDecl>(Decl)) {
166       if (Var->isLocalVarDeclOrParm())
167         return true;
168     }
169   } else if (isa<CXXThisExpr>(Statement))
170     return true;
171 
172   return any_of(Statement->children(), anyDescendantIsLocal);
173 }
174 
175 static bool tryCaptureAsLocalVariable(const MatchFinder::MatchResult &Result,
176                                       BindArgument &B, const Expr *E) {
177   if (const auto *BTE = dyn_cast<CXXBindTemporaryExpr>(E)) {
178     if (const auto *CE = dyn_cast<CXXConstructExpr>(BTE->getSubExpr()))
179       return tryCaptureAsLocalVariable(Result, B, CE->getArg(0));
180     return false;
181   }
182 
183   const auto *DRE = dyn_cast<DeclRefExpr>(E->IgnoreImplicit());
184   if (!DRE)
185     return false;
186 
187   const auto *VD = dyn_cast<VarDecl>(DRE->getDecl());
188   if (!VD || !VD->isLocalVarDeclOrParm())
189     return false;
190 
191   B.CM = CM_ByValue;
192   B.UsageIdentifier = std::string(getSourceTextForExpr(Result, E));
193   B.CaptureIdentifier = B.UsageIdentifier;
194   return true;
195 }
196 
197 static bool tryCaptureAsMemberVariable(const MatchFinder::MatchResult &Result,
198                                        BindArgument &B, const Expr *E) {
199   if (const auto *BTE = dyn_cast<CXXBindTemporaryExpr>(E)) {
200     if (const auto *CE = dyn_cast<CXXConstructExpr>(BTE->getSubExpr()))
201       return tryCaptureAsMemberVariable(Result, B, CE->getArg(0));
202     return false;
203   }
204 
205   E = E->IgnoreImplicit();
206   if (isa<CXXThisExpr>(E)) {
207     B.CM = CM_ByValue;
208     B.UsageIdentifier = std::string(getSourceTextForExpr(Result, E));
209     B.CaptureIdentifier = "this";
210     return true;
211   }
212 
213   const auto *ME = dyn_cast<MemberExpr>(E);
214   if (!ME)
215     return false;
216 
217   if (!ME->isLValue() || !isa<FieldDecl>(ME->getMemberDecl()))
218     return false;
219 
220   B.CM = CM_ByValue;
221   B.UsageIdentifier = std::string(getSourceTextForExpr(Result, E));
222   B.CaptureIdentifier = "this";
223   return true;
224 }
225 
226 static SmallVector<BindArgument, 4>
227 buildBindArguments(const MatchFinder::MatchResult &Result,
228                    const CallableInfo &Callable) {
229   SmallVector<BindArgument, 4> BindArguments;
230   llvm::Regex MatchPlaceholder("^_([0-9]+)$");
231 
232   const auto *BindCall = Result.Nodes.getNodeAs<CallExpr>("bind");
233 
234   // Start at index 1 as first argument to bind is the function name.
235   unsigned CaptureIndex = 0;
236   for (size_t I = 1, ArgCount = BindCall->getNumArgs(); I < ArgCount; ++I) {
237 
238     const Expr *E = BindCall->getArg(I);
239     BindArgument &B = BindArguments.emplace_back();
240 
241     size_t ArgIndex = I - 1;
242     if (Callable.Type == CT_MemberFunction)
243       --ArgIndex;
244 
245     bool IsObjectPtr = (I == 1 && Callable.Type == CT_MemberFunction);
246     B.E = E;
247     B.SourceTokens = getSourceTextForExpr(Result, E);
248 
249     if (!Callable.Decl || ArgIndex < Callable.Decl->getNumParams() ||
250         IsObjectPtr)
251       B.IsUsed = true;
252 
253     SmallVector<StringRef, 2> Matches;
254     if (MatchPlaceholder.match(B.SourceTokens, &Matches)) {
255       B.Kind = BK_Placeholder;
256       B.PlaceHolderIndex = std::stoi(std::string(Matches[1]));
257       B.UsageIdentifier = "PH" + llvm::utostr(B.PlaceHolderIndex);
258       B.CaptureIdentifier = B.UsageIdentifier;
259       continue;
260     }
261 
262     if (const auto *CE =
263             dyn_cast<CallExpr>(ignoreTemporariesAndConstructors(E))) {
264       initializeBindArgumentForCallExpr(Result, B, CE, CaptureIndex);
265       continue;
266     }
267 
268     if (tryCaptureAsLocalVariable(Result, B, B.E) ||
269         tryCaptureAsMemberVariable(Result, B, B.E))
270       continue;
271 
272     // If it's not something we recognize, capture it by init expression to be
273     // safe.
274     B.Kind = BK_Other;
275     if (IsObjectPtr) {
276       B.CM = CM_InitExpression;
277       B.UsageIdentifier = "ObjectPtr";
278       B.CaptureIdentifier = B.UsageIdentifier;
279     } else if (anyDescendantIsLocal(B.E)) {
280       B.CM = CM_InitExpression;
281       B.CaptureIdentifier = "capture" + llvm::utostr(CaptureIndex++);
282       B.UsageIdentifier = B.CaptureIdentifier;
283     }
284   }
285   return BindArguments;
286 }
287 
288 static int findPositionOfPlaceholderUse(ArrayRef<BindArgument> Args,
289                                         size_t PlaceholderIndex) {
290   for (size_t I = 0; I < Args.size(); ++I)
291     if (Args[I].PlaceHolderIndex == PlaceholderIndex)
292       return I;
293 
294   return -1;
295 }
296 
297 static void addPlaceholderArgs(const LambdaProperties &LP,
298                                llvm::raw_ostream &Stream,
299                                bool PermissiveParameterList) {
300 
301   ArrayRef<BindArgument> Args = LP.BindArguments;
302 
303   auto MaxPlaceholderIt =
304       std::max_element(Args.begin(), Args.end(),
305                        [](const BindArgument &B1, const BindArgument &B2) {
306                          return B1.PlaceHolderIndex < B2.PlaceHolderIndex;
307                        });
308 
309   // Placeholders (if present) have index 1 or greater.
310   if (!PermissiveParameterList && (MaxPlaceholderIt == Args.end() ||
311                                    MaxPlaceholderIt->PlaceHolderIndex == 0))
312     return;
313 
314   size_t PlaceholderCount = MaxPlaceholderIt->PlaceHolderIndex;
315   Stream << "(";
316   StringRef Delimiter = "";
317   for (size_t I = 1; I <= PlaceholderCount; ++I) {
318     Stream << Delimiter << "auto &&";
319 
320     int ArgIndex = findPositionOfPlaceholderUse(Args, I);
321 
322     if (ArgIndex != -1 && Args[ArgIndex].IsUsed)
323       Stream << " " << Args[ArgIndex].UsageIdentifier;
324     Delimiter = ", ";
325   }
326   if (PermissiveParameterList)
327     Stream << Delimiter << "auto && ...";
328   Stream << ")";
329 }
330 
331 static void addFunctionCallArgs(ArrayRef<BindArgument> Args,
332                                 llvm::raw_ostream &Stream) {
333   StringRef Delimiter = "";
334 
335   for (int I = 0, Size = Args.size(); I < Size; ++I) {
336     const BindArgument &B = Args[I];
337 
338     Stream << Delimiter;
339 
340     if (B.Kind == BK_Placeholder || B.CM != CM_None)
341       Stream << B.UsageIdentifier;
342     else if (B.CM == CM_None)
343       Stream << B.SourceTokens;
344 
345     Delimiter = ", ";
346   }
347 }
348 
349 static bool isPlaceHolderIndexRepeated(const ArrayRef<BindArgument> Args) {
350   llvm::SmallSet<size_t, 4> PlaceHolderIndices;
351   for (const BindArgument &B : Args) {
352     if (B.PlaceHolderIndex) {
353       if (!PlaceHolderIndices.insert(B.PlaceHolderIndex).second)
354         return true;
355     }
356   }
357   return false;
358 }
359 
360 static std::vector<const CXXMethodDecl *>
361 findCandidateCallOperators(const CXXRecordDecl *RecordDecl, size_t NumArgs) {
362   std::vector<const CXXMethodDecl *> Candidates;
363 
364   for (const clang::CXXMethodDecl *Method : RecordDecl->methods()) {
365     OverloadedOperatorKind OOK = Method->getOverloadedOperator();
366 
367     if (OOK != OverloadedOperatorKind::OO_Call)
368       continue;
369 
370     if (Method->getNumParams() > NumArgs)
371       continue;
372 
373     Candidates.push_back(Method);
374   }
375 
376   return Candidates;
377 }
378 
379 static bool isFixitSupported(const CallableInfo &Callee,
380                              ArrayRef<BindArgument> Args) {
381   // Do not attempt to create fixits for nested std::bind or std::ref.
382   // Supporting nested std::bind will be more difficult due to placeholder
383   // sharing between outer and inner std::bind invocations, and std::ref
384   // requires us to capture some parameters by reference instead of by value.
385   if (any_of(Args, [](const BindArgument &B) {
386         return isCallExprNamed(B.E, "boost::bind") ||
387                isCallExprNamed(B.E, "std::bind");
388       })) {
389     return false;
390   }
391 
392   // Do not attempt to create fixits when placeholders are reused.
393   // Unused placeholders are supported by requiring C++14 generic lambdas.
394   // FIXME: Support this case by deducing the common type.
395   if (isPlaceHolderIndexRepeated(Args))
396     return false;
397 
398   // If we can't determine the Decl being used, don't offer a fixit.
399   if (!Callee.Decl)
400     return false;
401 
402   if (Callee.Type == CT_Other || Callee.Materialization == CMK_Other)
403     return false;
404 
405   return true;
406 }
407 
408 const FunctionDecl *getCallOperator(const CXXRecordDecl *Callable,
409                                     size_t NumArgs) {
410   std::vector<const CXXMethodDecl *> Candidates =
411       findCandidateCallOperators(Callable, NumArgs);
412   if (Candidates.size() != 1)
413     return nullptr;
414 
415   return Candidates.front();
416 }
417 
418 const FunctionDecl *
419 getCallMethodDecl(const MatchFinder::MatchResult &Result, CallableType Type,
420                   CallableMaterializationKind Materialization) {
421 
422   const Expr *Callee = Result.Nodes.getNodeAs<Expr>("ref");
423   const Expr *CallExpression = ignoreTemporariesAndPointers(Callee);
424 
425   if (Type == CT_Object) {
426     const auto *BindCall = Result.Nodes.getNodeAs<CallExpr>("bind");
427     size_t NumArgs = BindCall->getNumArgs() - 1;
428     return getCallOperator(Callee->getType()->getAsCXXRecordDecl(), NumArgs);
429   }
430 
431   if (Materialization == CMK_Function) {
432     if (const auto *DRE = dyn_cast<DeclRefExpr>(CallExpression))
433       return dyn_cast<FunctionDecl>(DRE->getDecl());
434   }
435 
436   // Maybe this is an indirect call through a function pointer or something
437   // where we can't determine the exact decl.
438   return nullptr;
439 }
440 
441 static CallableType getCallableType(const MatchFinder::MatchResult &Result) {
442   const auto *CallableExpr = Result.Nodes.getNodeAs<Expr>("ref");
443 
444   QualType QT = CallableExpr->getType();
445   if (QT->isMemberFunctionPointerType())
446     return CT_MemberFunction;
447 
448   if (QT->isFunctionPointerType() || QT->isFunctionReferenceType() ||
449       QT->isFunctionType())
450     return CT_Function;
451 
452   if (QT->isRecordType()) {
453     const CXXRecordDecl *Decl = QT->getAsCXXRecordDecl();
454     if (!Decl)
455       return CT_Other;
456 
457     return CT_Object;
458   }
459 
460   return CT_Other;
461 }
462 
463 static CallableMaterializationKind
464 getCallableMaterialization(const MatchFinder::MatchResult &Result) {
465   const auto *CallableExpr = Result.Nodes.getNodeAs<Expr>("ref");
466 
467   const auto *NoTemporaries = ignoreTemporariesAndPointers(CallableExpr);
468 
469   if (isa<CallExpr>(NoTemporaries))
470     return CMK_CallExpression;
471 
472   if (isa<CXXFunctionalCastExpr>(NoTemporaries) ||
473       isa<CXXConstructExpr>(NoTemporaries))
474     return CMK_Function;
475 
476   if (const auto *DRE = dyn_cast<DeclRefExpr>(NoTemporaries)) {
477     if (isa<FunctionDecl>(DRE->getDecl()))
478       return CMK_Function;
479     if (isa<VarDecl>(DRE->getDecl()))
480       return CMK_VariableRef;
481   }
482 
483   return CMK_Other;
484 }
485 
486 static LambdaProperties
487 getLambdaProperties(const MatchFinder::MatchResult &Result) {
488   const auto *CalleeExpr = Result.Nodes.getNodeAs<Expr>("ref");
489 
490   LambdaProperties LP;
491 
492   const auto *Bind = Result.Nodes.getNodeAs<CallExpr>("bind");
493   const auto *Decl = dyn_cast<FunctionDecl>(Bind->getCalleeDecl());
494   const auto *NS =
495       dyn_cast<NamespaceDecl>(Decl->getEnclosingNamespaceContext());
496   while (NS->isInlineNamespace())
497     NS = dyn_cast<NamespaceDecl>(NS->getDeclContext());
498   LP.BindNamespace = NS->getName();
499 
500   LP.Callable.Type = getCallableType(Result);
501   LP.Callable.Materialization = getCallableMaterialization(Result);
502   LP.Callable.Decl =
503       getCallMethodDecl(Result, LP.Callable.Type, LP.Callable.Materialization);
504   LP.Callable.SourceTokens = getSourceTextForExpr(Result, CalleeExpr);
505   if (LP.Callable.Materialization == CMK_VariableRef) {
506     LP.Callable.CM = CM_ByValue;
507     LP.Callable.UsageIdentifier =
508         std::string(getSourceTextForExpr(Result, CalleeExpr));
509     LP.Callable.CaptureIdentifier = std::string(
510         getSourceTextForExpr(Result, ignoreTemporariesAndPointers(CalleeExpr)));
511   } else if (LP.Callable.Materialization == CMK_CallExpression) {
512     LP.Callable.CM = CM_InitExpression;
513     LP.Callable.UsageIdentifier = "Func";
514     LP.Callable.CaptureIdentifier = "Func";
515     LP.Callable.CaptureInitializer = getSourceTextForExpr(Result, CalleeExpr);
516   }
517 
518   LP.BindArguments = buildBindArguments(Result, LP.Callable);
519 
520   LP.IsFixitSupported = isFixitSupported(LP.Callable, LP.BindArguments);
521 
522   return LP;
523 }
524 
525 static bool emitCapture(llvm::StringSet<> &CaptureSet, StringRef Delimiter,
526                         CaptureMode CM, StringRef Identifier,
527                         StringRef InitExpression, raw_ostream &Stream) {
528   if (CM == CM_None)
529     return false;
530 
531   // This capture has already been emitted.
532   if (CaptureSet.count(Identifier) != 0)
533     return false;
534 
535   Stream << Delimiter;
536 
537   if (CM == CM_ByRef)
538     Stream << "&";
539   Stream << Identifier;
540   if (CM == CM_InitExpression)
541     Stream << " = " << InitExpression;
542 
543   CaptureSet.insert(Identifier);
544   return true;
545 }
546 
547 static void emitCaptureList(const LambdaProperties &LP,
548                             const MatchFinder::MatchResult &Result,
549                             raw_ostream &Stream) {
550   llvm::StringSet<> CaptureSet;
551   bool AnyCapturesEmitted = false;
552 
553   AnyCapturesEmitted = emitCapture(CaptureSet, "", LP.Callable.CM,
554                                    LP.Callable.CaptureIdentifier,
555                                    LP.Callable.CaptureInitializer, Stream);
556 
557   for (const BindArgument &B : LP.BindArguments) {
558     if (B.CM == CM_None || !B.IsUsed)
559       continue;
560 
561     StringRef Delimiter = AnyCapturesEmitted ? ", " : "";
562 
563     if (emitCapture(CaptureSet, Delimiter, B.CM, B.CaptureIdentifier,
564                     B.SourceTokens, Stream))
565       AnyCapturesEmitted = true;
566   }
567 }
568 
569 static ArrayRef<BindArgument>
570 getForwardedArgumentList(const LambdaProperties &P) {
571   ArrayRef<BindArgument> Args = makeArrayRef(P.BindArguments);
572   if (P.Callable.Type != CT_MemberFunction)
573     return Args;
574 
575   return Args.drop_front();
576 }
577 AvoidBindCheck::AvoidBindCheck(StringRef Name, ClangTidyContext *Context)
578     : ClangTidyCheck(Name, Context),
579       PermissiveParameterList(Options.get("PermissiveParameterList", 0) != 0) {}
580 
581 void AvoidBindCheck::registerMatchers(MatchFinder *Finder) {
582   Finder->addMatcher(
583       callExpr(
584           callee(namedDecl(
585               anyOf(hasName("::boost::bind"), hasName("::std::bind")))),
586           hasArgument(
587               0, anyOf(expr(hasType(memberPointerType())).bind("ref"),
588                        expr(hasParent(materializeTemporaryExpr().bind("ref"))),
589                        expr().bind("ref"))))
590           .bind("bind"),
591       this);
592 }
593 
594 void AvoidBindCheck::check(const MatchFinder::MatchResult &Result) {
595   const auto *MatchedDecl = Result.Nodes.getNodeAs<CallExpr>("bind");
596 
597   LambdaProperties LP = getLambdaProperties(Result);
598   auto Diag =
599       diag(MatchedDecl->getBeginLoc(),
600            formatv("prefer a lambda to {0}::bind", LP.BindNamespace).str());
601   if (!LP.IsFixitSupported)
602     return;
603 
604   const auto *Ref = Result.Nodes.getNodeAs<Expr>("ref");
605 
606   std::string Buffer;
607   llvm::raw_string_ostream Stream(Buffer);
608 
609   Stream << "[";
610   emitCaptureList(LP, Result, Stream);
611   Stream << "]";
612 
613   ArrayRef<BindArgument> FunctionCallArgs = makeArrayRef(LP.BindArguments);
614 
615   addPlaceholderArgs(LP, Stream, PermissiveParameterList);
616 
617   if (LP.Callable.Type == CT_Function) {
618     StringRef SourceTokens = LP.Callable.SourceTokens;
619     SourceTokens.consume_front("&");
620     Stream << " { return " << SourceTokens;
621   } else if (LP.Callable.Type == CT_MemberFunction) {
622     const auto *MethodDecl = dyn_cast<CXXMethodDecl>(LP.Callable.Decl);
623     const BindArgument &ObjPtr = FunctionCallArgs.front();
624 
625     Stream << " { ";
626     if (!isa<CXXThisExpr>(ignoreTemporariesAndPointers(ObjPtr.E))) {
627       Stream << ObjPtr.UsageIdentifier;
628       Stream << "->";
629     }
630 
631     Stream << MethodDecl->getName();
632   } else {
633     Stream << " { return ";
634     switch (LP.Callable.CM) {
635     case CM_ByValue:
636     case CM_ByRef:
637       if (LP.Callable.UsageIdentifier != LP.Callable.CaptureIdentifier) {
638         Stream << "(" << LP.Callable.UsageIdentifier << ")";
639         break;
640       }
641       LLVM_FALLTHROUGH;
642     case CM_InitExpression:
643       Stream << LP.Callable.UsageIdentifier;
644       break;
645     default:
646       Ref->printPretty(Stream, nullptr, Result.Context->getPrintingPolicy());
647     }
648   }
649 
650   Stream << "(";
651 
652   addFunctionCallArgs(getForwardedArgumentList(LP), Stream);
653   Stream << "); }";
654 
655   Diag << FixItHint::CreateReplacement(MatchedDecl->getSourceRange(),
656                                        Stream.str());
657 }
658 
659 } // namespace modernize
660 } // namespace tidy
661 } // namespace clang
662