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