clang-tools  12.0.0git
AvoidBindCheck.cpp
Go to the documentation of this file.
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 
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 
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 
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)
125 
126  return E;
127 }
128 
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)
136 
137  return E;
138 }
139 
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 
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
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 
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 
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 
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>
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 
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 
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 
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 
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 *>
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 
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 
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 *
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 
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
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
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(
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 
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 
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>
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 }
637 AvoidBindCheck::AvoidBindCheck(StringRef Name, ClangTidyContext *Context)
638  : ClangTidyCheck(Name, Context),
639  PermissiveParameterList(Options.get("PermissiveParameterList", false)) {}
640 
642  Options.store(Opts, "PermissiveParameterList", PermissiveParameterList);
643 }
644 
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 
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 
715  Stream << "); }";
716 
717  Diag << FixItHint::CreateReplacement(MatchedDecl->getSourceRange(),
718  Stream.str());
719 }
720 
721 } // namespace modernize
722 } // namespace tidy
723 } // namespace clang
const FunctionDecl * getCallOperator(const CXXRecordDecl *Callable, size_t NumArgs)
static std::vector< const FunctionDecl * > findCandidateCallOperators(const CXXRecordDecl *RecordDecl, size_t NumArgs)
static CallableType getCallableType(const MatchFinder::MatchResult &Result)
const FunctionDecl * Decl
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
static void addFunctionCallArgs(ArrayRef< BindArgument > Args, llvm::raw_ostream &Stream)
StringRef CaptureInitializer
CallableInfo Callable
SmallVector< BindArgument, 4 > BindArguments
static void initializeBindArgumentForCallExpr(const MatchFinder::MatchResult &Result, BindArgument &B, const CallExpr *CE, unsigned &CaptureIndex)
static bool tryCaptureAsLocalVariable(const MatchFinder::MatchResult &Result, BindArgument &B, const Expr *E)
static SmallVector< BindArgument, 4 > buildBindArguments(const MatchFinder::MatchResult &Result, const CallableInfo &Callable)
static const Expr * ignoreTemporariesAndConstructors(const Expr *E)
bool IsFixitSupported
CallableMaterializationKind Materialization
Base class for all clang-tidy checks.
static bool anyDescendantIsLocal(const Stmt *Statement)
static bool isPlaceHolderIndexRepeated(const ArrayRef< BindArgument > Args)
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
static void addPlaceholderArgs(const LambdaProperties &LP, llvm::raw_ostream &Stream, bool PermissiveParameterList)
BindArgumentKind Kind
static bool tryCaptureAsMemberVariable(const MatchFinder::MatchResult &Result, BindArgument &B, const Expr *E)
std::string UsageIdentifier
static ArrayRef< BindArgument > getForwardedArgumentList(const LambdaProperties &P)
StringRef BindNamespace
const FunctionDecl * getCallMethodDecl(const MatchFinder::MatchResult &Result, CallableType Type, CallableMaterializationKind Materialization)
StringRef SourceTokens
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
static int findPositionOfPlaceholderUse(ArrayRef< BindArgument > Args, size_t PlaceholderIndex)
static constexpr llvm::StringLiteral Name
static LambdaProperties getLambdaProperties(const MatchFinder::MatchResult &Result)
static bool isFixitSupported(const CallableInfo &Callee, ArrayRef< BindArgument > Args)
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
static CallableMaterializationKind getCallableMaterialization(const MatchFinder::MatchResult &Result)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
std::string CaptureIdentifier
CallableType Type
static StringRef getSourceTextForExpr(const MatchFinder::MatchResult &Result, const Expr *E)
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
const Expr * E
CaptureExpr CE
CaptureMode CM
static bool isCallExprNamed(const Expr *E, StringRef Name)
bool IsUsed
std::map< std::string, ClangTidyValue > OptionMap
static bool emitCapture(llvm::StringSet<> &CaptureSet, StringRef Delimiter, CaptureMode CM, CaptureExpr CE, StringRef Identifier, StringRef InitExpression, raw_ostream &Stream)
const DeclRefExpr * DeclRef
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check&#39;s name.
static const Expr * ignoreTemporariesAndPointers(const Expr *E)
static void emitCaptureList(const LambdaProperties &LP, const MatchFinder::MatchResult &Result, raw_ostream &Stream)
size_t PlaceHolderIndex