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