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