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