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