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