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
30using namespace clang::ast_matchers;
31
33
34namespace {
35
36enum BindArgumentKind { BK_Temporary, BK_Placeholder, BK_CallExpr, BK_Other };
37enum CaptureMode { CM_None, CM_ByRef, CM_ByValue };
38enum CaptureExpr { CE_None, CE_Var, CE_InitExpression };
39
40enum 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
47enum 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
55struct 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
89struct 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;
98 const FunctionDecl *Decl = nullptr;
99};
100
101struct LambdaProperties {
102 CallableInfo Callable;
103 SmallVector<BindArgument, 4> BindArguments;
104 StringRef BindNamespace;
105 bool IsFixitSupported = false;
106};
107
108} // end namespace
109
110static bool tryCaptureAsLocalVariable(const MatchFinder::MatchResult &Result,
111 BindArgument &B, const Expr *E);
112
113static bool tryCaptureAsMemberVariable(const MatchFinder::MatchResult &Result,
114 BindArgument &B, const Expr *E);
115
116static 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
127static 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
138static 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
145static 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
155static void
156initializeBindArgumentForCallExpr(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
183static 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
196static 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
218static 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
253static SmallVector<BindArgument, 4>
254buildBindArguments(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
320static 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
329static 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
363static 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
384static 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
395static std::vector<const FunctionDecl *>
396findCandidateCallOperators(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
431static 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
460const 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
470const FunctionDecl *
471getCallMethodDecl(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
493static 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
515static CallableMaterializationKind
516getCallableMaterialization(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
542static LambdaProperties
543getLambdaProperties(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
582static 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
604static 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
626static ArrayRef<BindArgument>
627getForwardedArgumentList(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}
635 : ClangTidyCheck(Name, Context),
636 PermissiveParameterList(Options.get("PermissiveParameterList", false)) {}
637
639 Options.store(Opts, "PermissiveParameterList", PermissiveParameterList);
640}
641
642void 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
654void 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
StringRef SourceTokens
const Expr * E
bool IsUsed
SmallVector< BindArgument, 4 > BindArguments
CaptureMode CM
size_t PlaceHolderIndex
const FunctionDecl * Decl
CallableInfo Callable
StringRef CaptureInitializer
BindArgumentKind Kind
CaptureExpr CE
CallableMaterializationKind Materialization
std::string UsageIdentifier
bool IsFixitSupported
StringRef BindNamespace
std::string CaptureIdentifier
NodeType Type
Token Name
llvm::json::Object Args
Definition: Trace.cpp:138
const DeclRefExpr * DeclRef
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.
Base class for all clang-tidy checks.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
AvoidBindCheck(StringRef Name, ClangTidyContext *Context)
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
static bool tryCaptureAsMemberVariable(const MatchFinder::MatchResult &Result, BindArgument &B, const Expr *E)
static void initializeBindArgumentForCallExpr(const MatchFinder::MatchResult &Result, BindArgument &B, const CallExpr *CE, unsigned &CaptureIndex)
static constexpr char Bind[]
static bool emitCapture(llvm::StringSet<> &CaptureSet, StringRef Delimiter, CaptureMode CM, CaptureExpr CE, StringRef Identifier, StringRef InitExpression, raw_ostream &Stream)
static bool tryCaptureAsLocalVariable(const MatchFinder::MatchResult &Result, BindArgument &B, const Expr *E)
static bool anyDescendantIsLocal(const Stmt *Statement)
static int findPositionOfPlaceholderUse(ArrayRef< BindArgument > Args, size_t PlaceholderIndex)
static void addPlaceholderArgs(const LambdaProperties &LP, llvm::raw_ostream &Stream, bool PermissiveParameterList)
static SmallVector< BindArgument, 4 > buildBindArguments(const MatchFinder::MatchResult &Result, const CallableInfo &Callable)
static StringRef getSourceTextForExpr(const MatchFinder::MatchResult &Result, const Expr *E)
const FunctionDecl * getCallOperator(const CXXRecordDecl *Callable, size_t NumArgs)
static CallableType getCallableType(const MatchFinder::MatchResult &Result)
static void emitCaptureList(const LambdaProperties &LP, const MatchFinder::MatchResult &Result, raw_ostream &Stream)
static void addFunctionCallArgs(ArrayRef< BindArgument > Args, llvm::raw_ostream &Stream)
static bool isPlaceHolderIndexRepeated(const ArrayRef< BindArgument > Args)
static const Expr * ignoreTemporariesAndPointers(const Expr *E)
static bool isCallExprNamed(const Expr *E, StringRef Name)
static const Expr * ignoreTemporariesAndConstructors(const Expr *E)
static LambdaProperties getLambdaProperties(const MatchFinder::MatchResult &Result)
const FunctionDecl * getCallMethodDecl(const MatchFinder::MatchResult &Result, CallableType Type, CallableMaterializationKind Materialization)
static ArrayRef< BindArgument > getForwardedArgumentList(const LambdaProperties &P)
static std::vector< const FunctionDecl * > findCandidateCallOperators(const CXXRecordDecl *RecordDecl, size_t NumArgs)
static CallableMaterializationKind getCallableMaterialization(const MatchFinder::MatchResult &Result)
static bool isFixitSupported(const CallableInfo &Callee, ArrayRef< BindArgument > Args)
llvm::StringMap< ClangTidyValue > OptionMap