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