1 //===--- AvoidBindCheck.cpp - clang-tidy-----------------------------------===//
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
7 //===----------------------------------------------------------------------===//
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"
30 using namespace clang::ast_matchers
;
32 namespace clang::tidy::modernize
{
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
};
42 CT_Function
, // global or static function
43 CT_MemberFunction
, // member function with implicit this
44 CT_Object
, // object with operator()
47 enum CallableMaterializationKind
{
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
52 CMK_CallExpression
, // callable is obtained as the result of a call expression
56 // A rough classification of the type of expression this argument was.
57 BindArgumentKind Kind
= BK_Other
;
59 // If this argument required a capture, a value indicating how it was
61 CaptureMode CM
= CM_None
;
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
;
67 // The exact spelling of this argument in the source code.
68 StringRef SourceTokens
;
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
;
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
;
79 // If Kind == BK_Placeholder, the index of the placeholder.
80 size_t PlaceHolderIndex
= 0;
82 // True if the argument is used inside the lambda, false otherwise.
85 // The actual Expr object representing this expression.
86 const Expr
*E
= nullptr;
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 bool DoesReturn
= false;
102 struct LambdaProperties
{
103 CallableInfo Callable
;
104 SmallVector
<BindArgument
, 4> BindArguments
;
105 StringRef BindNamespace
;
106 bool IsFixitSupported
= false;
111 static bool tryCaptureAsLocalVariable(const MatchFinder::MatchResult
&Result
,
112 BindArgument
&B
, const Expr
*E
);
114 static bool tryCaptureAsMemberVariable(const MatchFinder::MatchResult
&Result
,
115 BindArgument
&B
, const Expr
*E
);
117 static const Expr
*ignoreTemporariesAndPointers(const Expr
*E
) {
118 if (const auto *T
= dyn_cast
<UnaryOperator
>(E
))
119 return ignoreTemporariesAndPointers(T
->getSubExpr());
121 const Expr
*F
= E
->IgnoreImplicit();
123 return ignoreTemporariesAndPointers(F
);
128 static const Expr
*ignoreTemporariesAndConstructors(const Expr
*E
) {
129 if (const auto *T
= dyn_cast
<CXXConstructExpr
>(E
))
130 return ignoreTemporariesAndConstructors(T
->getArg(0));
132 const Expr
*F
= E
->IgnoreImplicit();
134 return ignoreTemporariesAndPointers(F
);
139 static StringRef
getSourceTextForExpr(const MatchFinder::MatchResult
&Result
,
141 return Lexer::getSourceText(
142 CharSourceRange::getTokenRange(E
->getBeginLoc(), E
->getEndLoc()),
143 *Result
.SourceManager
, Result
.Context
->getLangOpts());
146 static bool isCallExprNamed(const Expr
*E
, StringRef Name
) {
147 const auto *CE
= dyn_cast
<CallExpr
>(E
->IgnoreImplicit());
150 const auto *ND
= dyn_cast
<NamedDecl
>(CE
->getCalleeDecl());
153 return ND
->getQualifiedNameAsString() == Name
;
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")) {
163 if (tryCaptureAsLocalVariable(Result
, B
, CE
->getArg(0)) ||
164 tryCaptureAsMemberVariable(Result
, B
, CE
->getArg(0))) {
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
++);
172 // Strip off the reference wrapper.
173 B
.SourceTokens
= getSourceTextForExpr(Result
, CE
->getArg(0));
176 B
.Kind
= BK_CallExpr
;
178 B
.CE
= CE_InitExpression
;
179 B
.UsageIdentifier
= "capture" + llvm::utostr(CaptureIndex
++);
181 B
.CaptureIdentifier
= B
.UsageIdentifier
;
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())
191 } else if (isa
<CXXThisExpr
>(Statement
))
194 return any_of(Statement
->children(), anyDescendantIsLocal
);
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));
205 const auto *DRE
= dyn_cast
<DeclRefExpr
>(E
->IgnoreImplicit());
209 const auto *VD
= dyn_cast
<VarDecl
>(DRE
->getDecl());
210 if (!VD
|| !VD
->isLocalVarDeclOrParm())
214 B
.UsageIdentifier
= std::string(getSourceTextForExpr(Result
, E
));
215 B
.CaptureIdentifier
= B
.UsageIdentifier
;
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));
227 E
= E
->IgnoreImplicit();
228 if (isa
<CXXThisExpr
>(E
)) {
229 // E is a direct use of "this".
231 B
.UsageIdentifier
= std::string(getSourceTextForExpr(Result
, E
));
232 B
.CaptureIdentifier
= "this";
236 const auto *ME
= dyn_cast
<MemberExpr
>(E
);
240 if (!ME
->isLValue() || !isa
<FieldDecl
>(ME
->getMemberDecl()))
243 if (isa
<CXXThisExpr
>(ME
->getBase())) {
244 // E refers to a data member without an explicit "this".
246 B
.UsageIdentifier
= std::string(getSourceTextForExpr(Result
, E
));
247 B
.CaptureIdentifier
= "this";
254 static SmallVector
<BindArgument
, 4>
255 buildBindArguments(const MatchFinder::MatchResult
&Result
,
256 const CallableInfo
&Callable
) {
257 SmallVector
<BindArgument
, 4> BindArguments
;
258 static llvm::Regex
MatchPlaceholder("^_([0-9]+)$");
260 const auto *BindCall
= Result
.Nodes
.getNodeAs
<CallExpr
>("bind");
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
) {
266 const Expr
*E
= BindCall
->getArg(I
);
267 BindArgument
&B
= BindArguments
.emplace_back();
269 size_t ArgIndex
= I
- 1;
270 if (Callable
.Type
== CT_MemberFunction
)
273 bool IsObjectPtr
= (I
== 1 && Callable
.Type
== CT_MemberFunction
);
275 B
.SourceTokens
= getSourceTextForExpr(Result
, E
);
277 if (!Callable
.Decl
|| ArgIndex
< Callable
.Decl
->getNumParams() ||
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
;
294 dyn_cast
<CallExpr
>(ignoreTemporariesAndConstructors(E
))) {
295 initializeBindArgumentForCallExpr(Result
, B
, CE
, CaptureIndex
);
299 if (tryCaptureAsLocalVariable(Result
, B
, B
.E
) ||
300 tryCaptureAsMemberVariable(Result
, B
, B
.E
))
303 // If it's not something we recognize, capture it by init expression to be
307 B
.CE
= CE_InitExpression
;
309 B
.UsageIdentifier
= "ObjectPtr";
310 B
.CaptureIdentifier
= B
.UsageIdentifier
;
311 } else if (anyDescendantIsLocal(B
.E
)) {
312 B
.CE
= CE_InitExpression
;
314 B
.CaptureIdentifier
= "capture" + llvm::utostr(CaptureIndex
++);
315 B
.UsageIdentifier
= B
.CaptureIdentifier
;
318 return BindArguments
;
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
)
330 static void addPlaceholderArgs(const LambdaProperties
&LP
,
331 llvm::raw_ostream
&Stream
,
332 bool PermissiveParameterList
) {
334 ArrayRef
<BindArgument
> Args
= LP
.BindArguments
;
336 const auto *MaxPlaceholderIt
=
337 std::max_element(Args
.begin(), Args
.end(),
338 [](const BindArgument
&B1
, const BindArgument
&B2
) {
339 return B1
.PlaceHolderIndex
< B2
.PlaceHolderIndex
;
342 // Placeholders (if present) have index 1 or greater.
343 if (!PermissiveParameterList
&& (MaxPlaceholderIt
== Args
.end() ||
344 MaxPlaceholderIt
->PlaceHolderIndex
== 0))
347 size_t PlaceholderCount
= MaxPlaceholderIt
->PlaceHolderIndex
;
349 StringRef Delimiter
= "";
350 for (size_t I
= 1; I
<= PlaceholderCount
; ++I
) {
351 Stream
<< Delimiter
<< "auto &&";
353 int ArgIndex
= findPositionOfPlaceholderUse(Args
, I
);
355 if (ArgIndex
!= -1 && Args
[ArgIndex
].IsUsed
)
356 Stream
<< " " << Args
[ArgIndex
].UsageIdentifier
;
359 if (PermissiveParameterList
)
360 Stream
<< Delimiter
<< "auto && ...";
364 static void addFunctionCallArgs(ArrayRef
<BindArgument
> Args
,
365 llvm::raw_ostream
&Stream
) {
366 StringRef Delimiter
= "";
368 for (const BindArgument
&B
: Args
) {
371 if (B
.Kind
== BK_Placeholder
) {
372 Stream
<< "std::forward<decltype(" << B
.UsageIdentifier
<< ")>";
373 Stream
<< "(" << B
.UsageIdentifier
<< ")";
374 } else if (B
.CM
!= CM_None
)
375 Stream
<< B
.UsageIdentifier
;
377 Stream
<< B
.SourceTokens
;
383 static bool isPlaceHolderIndexRepeated(const ArrayRef
<BindArgument
> Args
) {
384 llvm::SmallSet
<size_t, 4> PlaceHolderIndices
;
385 for (const BindArgument
&B
: Args
) {
386 if (B
.PlaceHolderIndex
) {
387 if (!PlaceHolderIndices
.insert(B
.PlaceHolderIndex
).second
)
394 static std::vector
<const FunctionDecl
*>
395 findCandidateCallOperators(const CXXRecordDecl
*RecordDecl
, size_t NumArgs
) {
396 std::vector
<const FunctionDecl
*> Candidates
;
398 for (const clang::CXXMethodDecl
*Method
: RecordDecl
->methods()) {
399 OverloadedOperatorKind OOK
= Method
->getOverloadedOperator();
401 if (OOK
!= OverloadedOperatorKind::OO_Call
)
404 if (Method
->getNumParams() > NumArgs
)
407 Candidates
.push_back(Method
);
410 // Find templated operator(), if any.
411 for (const clang::Decl
*D
: RecordDecl
->decls()) {
412 const auto *FTD
= dyn_cast
<FunctionTemplateDecl
>(D
);
415 const FunctionDecl
*FD
= FTD
->getTemplatedDecl();
417 OverloadedOperatorKind OOK
= FD
->getOverloadedOperator();
418 if (OOK
!= OverloadedOperatorKind::OO_Call
)
421 if (FD
->getNumParams() > NumArgs
)
424 Candidates
.push_back(FD
);
430 static bool isFixitSupported(const CallableInfo
&Callee
,
431 ArrayRef
<BindArgument
> Args
) {
432 // Do not attempt to create fixits for nested std::bind or std::ref.
433 // Supporting nested std::bind will be more difficult due to placeholder
434 // sharing between outer and inner std::bind invocations, and std::ref
435 // requires us to capture some parameters by reference instead of by value.
436 if (any_of(Args
, [](const BindArgument
&B
) {
437 return isCallExprNamed(B
.E
, "boost::bind") ||
438 isCallExprNamed(B
.E
, "std::bind");
443 // Do not attempt to create fixits when placeholders are reused.
444 // Unused placeholders are supported by requiring C++14 generic lambdas.
445 // FIXME: Support this case by deducing the common type.
446 if (isPlaceHolderIndexRepeated(Args
))
449 // If we can't determine the Decl being used, don't offer a fixit.
453 if (Callee
.Type
== CT_Other
|| Callee
.Materialization
== CMK_Other
)
459 const FunctionDecl
*getCallOperator(const CXXRecordDecl
*Callable
,
461 std::vector
<const FunctionDecl
*> Candidates
=
462 findCandidateCallOperators(Callable
, NumArgs
);
463 if (Candidates
.size() != 1)
466 return Candidates
.front();
470 getCallMethodDecl(const MatchFinder::MatchResult
&Result
, CallableType Type
,
471 CallableMaterializationKind Materialization
) {
473 const Expr
*Callee
= Result
.Nodes
.getNodeAs
<Expr
>("ref");
474 const Expr
*CallExpression
= ignoreTemporariesAndPointers(Callee
);
476 if (Type
== CT_Object
) {
477 const auto *BindCall
= Result
.Nodes
.getNodeAs
<CallExpr
>("bind");
478 size_t NumArgs
= BindCall
->getNumArgs() - 1;
479 return getCallOperator(Callee
->getType()->getAsCXXRecordDecl(), NumArgs
);
482 if (Materialization
== CMK_Function
) {
483 if (const auto *DRE
= dyn_cast
<DeclRefExpr
>(CallExpression
))
484 return dyn_cast
<FunctionDecl
>(DRE
->getDecl());
487 // Maybe this is an indirect call through a function pointer or something
488 // where we can't determine the exact decl.
492 static CallableType
getCallableType(const MatchFinder::MatchResult
&Result
) {
493 const auto *CallableExpr
= Result
.Nodes
.getNodeAs
<Expr
>("ref");
495 QualType QT
= CallableExpr
->getType();
496 if (QT
->isMemberFunctionPointerType())
497 return CT_MemberFunction
;
499 if (QT
->isFunctionPointerType() || QT
->isFunctionReferenceType() ||
500 QT
->isFunctionType())
503 if (QT
->isRecordType()) {
504 const CXXRecordDecl
*Decl
= QT
->getAsCXXRecordDecl();
514 static CallableMaterializationKind
515 getCallableMaterialization(const MatchFinder::MatchResult
&Result
) {
516 const auto *CallableExpr
= Result
.Nodes
.getNodeAs
<Expr
>("ref");
518 const auto *NoTemporaries
= ignoreTemporariesAndPointers(CallableExpr
);
520 const auto *CE
= dyn_cast
<CXXConstructExpr
>(NoTemporaries
);
521 const auto *FC
= dyn_cast
<CXXFunctionalCastExpr
>(NoTemporaries
);
522 if ((isa
<CallExpr
>(NoTemporaries
)) || (CE
&& (CE
->getNumArgs() > 0)) ||
523 (FC
&& (FC
->getCastKind() == CK_ConstructorConversion
)))
524 // CE is something that looks like a call, with arguments - either
525 // a function call or a constructor invocation.
526 return CMK_CallExpression
;
528 if (isa
<CXXFunctionalCastExpr
>(NoTemporaries
) || CE
)
531 if (const auto *DRE
= dyn_cast
<DeclRefExpr
>(NoTemporaries
)) {
532 if (isa
<FunctionDecl
>(DRE
->getDecl()))
534 if (isa
<VarDecl
>(DRE
->getDecl()))
535 return CMK_VariableRef
;
541 static LambdaProperties
542 getLambdaProperties(const MatchFinder::MatchResult
&Result
) {
543 const auto *CalleeExpr
= Result
.Nodes
.getNodeAs
<Expr
>("ref");
547 const auto *Bind
= Result
.Nodes
.getNodeAs
<CallExpr
>("bind");
548 const auto *Decl
= cast
<FunctionDecl
>(Bind
->getCalleeDecl());
549 const auto *NS
= cast
<NamespaceDecl
>(Decl
->getEnclosingNamespaceContext());
550 while (NS
->isInlineNamespace())
551 NS
= cast
<NamespaceDecl
>(NS
->getDeclContext());
552 LP
.BindNamespace
= NS
->getName();
554 LP
.Callable
.Type
= getCallableType(Result
);
555 LP
.Callable
.Materialization
= getCallableMaterialization(Result
);
557 getCallMethodDecl(Result
, LP
.Callable
.Type
, LP
.Callable
.Materialization
);
558 if (LP
.Callable
.Decl
)
559 if (const Type
*ReturnType
=
560 LP
.Callable
.Decl
->getReturnType().getCanonicalType().getTypePtr())
561 LP
.Callable
.DoesReturn
= !ReturnType
->isVoidType();
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
);
578 LP
.BindArguments
= buildBindArguments(Result
, LP
.Callable
);
580 LP
.IsFixitSupported
= isFixitSupported(LP
.Callable
, LP
.BindArguments
);
585 static bool emitCapture(llvm::StringSet
<> &CaptureSet
, StringRef Delimiter
,
586 CaptureMode CM
, CaptureExpr CE
, StringRef Identifier
,
587 StringRef InitExpression
, raw_ostream
&Stream
) {
591 // This capture has already been emitted.
592 if (CaptureSet
.count(Identifier
) != 0)
599 Stream
<< Identifier
;
600 if (CE
== CE_InitExpression
)
601 Stream
<< " = " << InitExpression
;
603 CaptureSet
.insert(Identifier
);
607 static void emitCaptureList(const LambdaProperties
&LP
,
608 const MatchFinder::MatchResult
&Result
,
609 raw_ostream
&Stream
) {
610 llvm::StringSet
<> CaptureSet
;
611 bool AnyCapturesEmitted
= false;
613 AnyCapturesEmitted
= emitCapture(
614 CaptureSet
, "", LP
.Callable
.CM
, LP
.Callable
.CE
,
615 LP
.Callable
.CaptureIdentifier
, LP
.Callable
.CaptureInitializer
, Stream
);
617 for (const BindArgument
&B
: LP
.BindArguments
) {
618 if (B
.CM
== CM_None
|| !B
.IsUsed
)
621 StringRef Delimiter
= AnyCapturesEmitted
? ", " : "";
623 if (emitCapture(CaptureSet
, Delimiter
, B
.CM
, B
.CE
, B
.CaptureIdentifier
,
624 B
.SourceTokens
, Stream
))
625 AnyCapturesEmitted
= true;
629 static ArrayRef
<BindArgument
>
630 getForwardedArgumentList(const LambdaProperties
&P
) {
631 ArrayRef
<BindArgument
> Args
= ArrayRef(P
.BindArguments
);
632 if (P
.Callable
.Type
!= CT_MemberFunction
)
635 return Args
.drop_front();
637 AvoidBindCheck::AvoidBindCheck(StringRef Name
, ClangTidyContext
*Context
)
638 : ClangTidyCheck(Name
, Context
),
639 PermissiveParameterList(Options
.get("PermissiveParameterList", false)) {}
641 void AvoidBindCheck::storeOptions(ClangTidyOptions::OptionMap
&Opts
) {
642 Options
.store(Opts
, "PermissiveParameterList", PermissiveParameterList
);
645 void AvoidBindCheck::registerMatchers(MatchFinder
*Finder
) {
648 callee(namedDecl(hasAnyName("::boost::bind", "::std::bind"))),
650 0, anyOf(expr(hasType(memberPointerType())).bind("ref"),
651 expr(hasParent(materializeTemporaryExpr().bind("ref"))),
652 expr().bind("ref"))))
657 void AvoidBindCheck::check(const MatchFinder::MatchResult
&Result
) {
658 const auto *MatchedDecl
= Result
.Nodes
.getNodeAs
<CallExpr
>("bind");
660 LambdaProperties LP
= getLambdaProperties(Result
);
662 diag(MatchedDecl
->getBeginLoc(),
663 formatv("prefer a lambda to {0}::bind", LP
.BindNamespace
).str());
664 if (!LP
.IsFixitSupported
)
667 const auto *Ref
= Result
.Nodes
.getNodeAs
<Expr
>("ref");
670 llvm::raw_string_ostream
Stream(Buffer
);
673 emitCaptureList(LP
, Result
, Stream
);
676 ArrayRef
<BindArgument
> FunctionCallArgs
= ArrayRef(LP
.BindArguments
);
678 addPlaceholderArgs(LP
, Stream
, PermissiveParameterList
);
682 if (LP
.Callable
.DoesReturn
) {
686 if (LP
.Callable
.Type
== CT_Function
) {
687 StringRef SourceTokens
= LP
.Callable
.SourceTokens
;
688 SourceTokens
.consume_front("&");
689 Stream
<< SourceTokens
;
690 } else if (LP
.Callable
.Type
== CT_MemberFunction
) {
691 const auto *MethodDecl
= dyn_cast
<CXXMethodDecl
>(LP
.Callable
.Decl
);
692 const BindArgument
&ObjPtr
= FunctionCallArgs
.front();
694 if (!isa
<CXXThisExpr
>(ignoreTemporariesAndPointers(ObjPtr
.E
))) {
695 Stream
<< ObjPtr
.UsageIdentifier
;
699 Stream
<< MethodDecl
->getName();
701 switch (LP
.Callable
.CE
) {
703 if (LP
.Callable
.UsageIdentifier
!= LP
.Callable
.CaptureIdentifier
) {
704 Stream
<< "(" << LP
.Callable
.UsageIdentifier
<< ")";
708 case CE_InitExpression
:
709 Stream
<< LP
.Callable
.UsageIdentifier
;
712 Stream
<< getSourceTextForExpr(Result
, Ref
);
718 addFunctionCallArgs(getForwardedArgumentList(LP
), Stream
);
721 Diag
<< FixItHint::CreateReplacement(MatchedDecl
->getSourceRange(),
725 } // namespace clang::tidy::modernize