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