722 lines · cpp
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-exception6//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 29namespace clang::tidy::modernize {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, // unknown39 CT_Function, // global or static function40 CT_MemberFunction, // member function with implicit this41 CT_Object, // object with operator()42};43 44enum CallableMaterializationKind {45 CMK_Other, // unknown46 CMK_Function, // callable is the name of a member or non-member function.47 CMK_VariableRef, // callable is a simple expression involving a global or48 // local variable.49 CMK_CallExpression, // callable is obtained as the result of a call expression50};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 was57 // 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 be68 // different from UsageIdentifier for example in the expression *d, where the69 // 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 tokens73 // 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 namespace107 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)120 return ignoreTemporariesAndPointers(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)131 return ignoreTemporariesAndPointers(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 void154initializeBindArgumentForCallExpr(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 return any_of(Statement->children(), anyDescendantIsLocal);192}193 194static bool tryCaptureAsLocalVariable(const MatchFinder::MatchResult &Result,195 BindArgument &B, const Expr *E) {196 if (const auto *BTE = dyn_cast<CXXBindTemporaryExpr>(E)) {197 if (const auto *CE = dyn_cast<CXXConstructExpr>(BTE->getSubExpr()))198 return tryCaptureAsLocalVariable(Result, B, CE->getArg(0));199 return false;200 }201 202 const auto *DRE = dyn_cast<DeclRefExpr>(E->IgnoreImplicit());203 if (!DRE)204 return false;205 206 const auto *VD = dyn_cast<VarDecl>(DRE->getDecl());207 if (!VD || !VD->isLocalVarDeclOrParm())208 return false;209 210 B.CM = CM_ByValue;211 B.UsageIdentifier = std::string(getSourceTextForExpr(Result, E));212 B.CaptureIdentifier = B.UsageIdentifier;213 return true;214}215 216static bool tryCaptureAsMemberVariable(const MatchFinder::MatchResult &Result,217 BindArgument &B, const Expr *E) {218 if (const auto *BTE = dyn_cast<CXXBindTemporaryExpr>(E)) {219 if (const auto *CE = dyn_cast<CXXConstructExpr>(BTE->getSubExpr()))220 return tryCaptureAsMemberVariable(Result, B, CE->getArg(0));221 return false;222 }223 224 E = E->IgnoreImplicit();225 if (isa<CXXThisExpr>(E)) {226 // E is a direct use of "this".227 B.CM = CM_ByValue;228 B.UsageIdentifier = std::string(getSourceTextForExpr(Result, E));229 B.CaptureIdentifier = "this";230 return true;231 }232 233 const auto *ME = dyn_cast<MemberExpr>(E);234 if (!ME)235 return false;236 237 if (!ME->isLValue() || !isa<FieldDecl>(ME->getMemberDecl()))238 return false;239 240 if (isa<CXXThisExpr>(ME->getBase())) {241 // E refers to a data member without an explicit "this".242 B.CM = CM_ByValue;243 B.UsageIdentifier = std::string(getSourceTextForExpr(Result, E));244 B.CaptureIdentifier = "this";245 return true;246 }247 248 return false;249}250 251static SmallVector<BindArgument, 4>252buildBindArguments(const MatchFinder::MatchResult &Result,253 const CallableInfo &Callable) {254 SmallVector<BindArgument, 4> BindArguments;255 static const llvm::Regex MatchPlaceholder("^_([0-9]+)$");256 257 const auto *BindCall = Result.Nodes.getNodeAs<CallExpr>("bind");258 259 // Start at index 1 as first argument to bind is the function name.260 unsigned CaptureIndex = 0;261 for (size_t I = 1, ArgCount = BindCall->getNumArgs(); I < ArgCount; ++I) {262 const Expr *E = BindCall->getArg(I);263 BindArgument &B = BindArguments.emplace_back();264 265 size_t ArgIndex = I - 1;266 if (Callable.Type == CT_MemberFunction)267 --ArgIndex;268 269 const bool IsObjectPtr = (I == 1 && Callable.Type == CT_MemberFunction);270 B.E = E;271 B.SourceTokens = getSourceTextForExpr(Result, E);272 273 if (!Callable.Decl || ArgIndex < Callable.Decl->getNumParams() ||274 IsObjectPtr)275 B.IsUsed = true;276 277 SmallVector<StringRef, 2> Matches;278 const auto *DRE = dyn_cast<DeclRefExpr>(E);279 if (MatchPlaceholder.match(B.SourceTokens, &Matches) ||280 // Check for match with qualifiers removed.281 (DRE && MatchPlaceholder.match(DRE->getDecl()->getName(), &Matches))) {282 B.Kind = BK_Placeholder;283 B.PlaceHolderIndex = std::stoi(std::string(Matches[1]));284 B.UsageIdentifier = "PH" + llvm::utostr(B.PlaceHolderIndex);285 B.CaptureIdentifier = B.UsageIdentifier;286 continue;287 }288 289 if (const auto *CE =290 dyn_cast<CallExpr>(ignoreTemporariesAndConstructors(E))) {291 initializeBindArgumentForCallExpr(Result, B, CE, CaptureIndex);292 continue;293 }294 295 if (tryCaptureAsLocalVariable(Result, B, B.E) ||296 tryCaptureAsMemberVariable(Result, B, B.E))297 continue;298 299 // If it's not something we recognize, capture it by init expression to be300 // safe.301 B.Kind = BK_Other;302 if (IsObjectPtr) {303 B.CE = CE_InitExpression;304 B.CM = CM_ByValue;305 B.UsageIdentifier = "ObjectPtr";306 B.CaptureIdentifier = B.UsageIdentifier;307 } else if (anyDescendantIsLocal(B.E)) {308 B.CE = CE_InitExpression;309 B.CM = CM_ByValue;310 B.CaptureIdentifier = "capture" + llvm::utostr(CaptureIndex++);311 B.UsageIdentifier = B.CaptureIdentifier;312 }313 }314 return BindArguments;315}316 317static int findPositionOfPlaceholderUse(ArrayRef<BindArgument> Args,318 size_t PlaceholderIndex) {319 for (size_t I = 0; I < Args.size(); ++I)320 if (Args[I].PlaceHolderIndex == PlaceholderIndex)321 return I;322 323 return -1;324}325 326static void addPlaceholderArgs(const LambdaProperties &LP,327 llvm::raw_ostream &Stream,328 bool PermissiveParameterList) {329 ArrayRef<BindArgument> Args = LP.BindArguments;330 331 const auto *MaxPlaceholderIt = llvm::max_element(332 Args, [](const BindArgument &B1, const BindArgument &B2) {333 return B1.PlaceHolderIndex < B2.PlaceHolderIndex;334 });335 336 // Placeholders (if present) have index 1 or greater.337 if (!PermissiveParameterList && (MaxPlaceholderIt == Args.end() ||338 MaxPlaceholderIt->PlaceHolderIndex == 0))339 return;340 341 const size_t PlaceholderCount = MaxPlaceholderIt->PlaceHolderIndex;342 Stream << "(";343 StringRef Delimiter = "";344 for (size_t I = 1; I <= PlaceholderCount; ++I) {345 Stream << Delimiter << "auto &&";346 347 const int ArgIndex = findPositionOfPlaceholderUse(Args, I);348 349 if (ArgIndex != -1 && Args[ArgIndex].IsUsed)350 Stream << " " << Args[ArgIndex].UsageIdentifier;351 Delimiter = ", ";352 }353 if (PermissiveParameterList)354 Stream << Delimiter << "auto && ...";355 Stream << ")";356}357 358static void addFunctionCallArgs(ArrayRef<BindArgument> Args,359 llvm::raw_ostream &Stream) {360 StringRef Delimiter = "";361 362 for (const BindArgument &B : Args) {363 Stream << Delimiter;364 365 if (B.Kind == BK_Placeholder) {366 Stream << "std::forward<decltype(" << B.UsageIdentifier << ")>";367 Stream << "(" << B.UsageIdentifier << ")";368 } else if (B.CM != CM_None)369 Stream << B.UsageIdentifier;370 else371 Stream << B.SourceTokens;372 373 Delimiter = ", ";374 }375}376 377static bool isPlaceHolderIndexRepeated(const ArrayRef<BindArgument> Args) {378 llvm::SmallSet<size_t, 4> PlaceHolderIndices;379 for (const BindArgument &B : Args) {380 if (B.PlaceHolderIndex) {381 if (!PlaceHolderIndices.insert(B.PlaceHolderIndex).second)382 return true;383 }384 }385 return false;386}387 388static std::vector<const FunctionDecl *>389findCandidateCallOperators(const CXXRecordDecl *RecordDecl, size_t NumArgs) {390 std::vector<const FunctionDecl *> Candidates;391 392 for (const clang::CXXMethodDecl *Method : RecordDecl->methods()) {393 const OverloadedOperatorKind OOK = Method->getOverloadedOperator();394 395 if (OOK != OverloadedOperatorKind::OO_Call)396 continue;397 398 if (Method->getNumParams() > NumArgs)399 continue;400 401 Candidates.push_back(Method);402 }403 404 // Find templated operator(), if any.405 for (const clang::Decl *D : RecordDecl->decls()) {406 const auto *FTD = dyn_cast<FunctionTemplateDecl>(D);407 if (!FTD)408 continue;409 const FunctionDecl *FD = FTD->getTemplatedDecl();410 411 const OverloadedOperatorKind OOK = FD->getOverloadedOperator();412 if (OOK != OverloadedOperatorKind::OO_Call)413 continue;414 415 if (FD->getNumParams() > NumArgs)416 continue;417 418 Candidates.push_back(FD);419 }420 421 return Candidates;422}423 424static bool isFixitSupported(const CallableInfo &Callee,425 ArrayRef<BindArgument> Args) {426 // Do not attempt to create fixits for nested std::bind or std::ref.427 // Supporting nested std::bind will be more difficult due to placeholder428 // sharing between outer and inner std::bind invocations, and std::ref429 // requires us to capture some parameters by reference instead of by value.430 if (any_of(Args, [](const BindArgument &B) {431 return isCallExprNamed(B.E, "boost::bind") ||432 isCallExprNamed(B.E, "std::bind");433 })) {434 return false;435 }436 437 // Do not attempt to create fixits when placeholders are reused.438 // Unused placeholders are supported by requiring C++14 generic lambdas.439 // FIXME: Support this case by deducing the common type.440 if (isPlaceHolderIndexRepeated(Args))441 return false;442 443 // If we can't determine the Decl being used, don't offer a fixit.444 if (!Callee.Decl)445 return false;446 447 if (Callee.Type == CT_Other || Callee.Materialization == CMK_Other)448 return false;449 450 return true;451}452 453static const FunctionDecl *getCallOperator(const CXXRecordDecl *Callable,454 size_t NumArgs) {455 std::vector<const FunctionDecl *> Candidates =456 findCandidateCallOperators(Callable, NumArgs);457 if (Candidates.size() != 1)458 return nullptr;459 460 return Candidates.front();461}462 463static const FunctionDecl *464getCallMethodDecl(const MatchFinder::MatchResult &Result, CallableType Type,465 CallableMaterializationKind Materialization) {466 const Expr *Callee = Result.Nodes.getNodeAs<Expr>("ref");467 const Expr *CallExpression = ignoreTemporariesAndPointers(Callee);468 469 if (Type == CT_Object) {470 const auto *BindCall = Result.Nodes.getNodeAs<CallExpr>("bind");471 const size_t NumArgs = BindCall->getNumArgs() - 1;472 return getCallOperator(Callee->getType()->getAsCXXRecordDecl(), NumArgs);473 }474 475 if (Materialization == CMK_Function) {476 if (const auto *DRE = dyn_cast<DeclRefExpr>(CallExpression))477 return dyn_cast<FunctionDecl>(DRE->getDecl());478 }479 480 // Maybe this is an indirect call through a function pointer or something481 // where we can't determine the exact decl.482 return nullptr;483}484 485static CallableType getCallableType(const MatchFinder::MatchResult &Result) {486 const auto *CallableExpr = Result.Nodes.getNodeAs<Expr>("ref");487 488 const QualType QT = CallableExpr->getType();489 if (QT->isMemberFunctionPointerType())490 return CT_MemberFunction;491 492 if (QT->isFunctionPointerType() || QT->isFunctionReferenceType() ||493 QT->isFunctionType())494 return CT_Function;495 496 if (QT->isRecordType()) {497 const CXXRecordDecl *Decl = QT->getAsCXXRecordDecl();498 if (!Decl)499 return CT_Other;500 501 return CT_Object;502 }503 504 return CT_Other;505}506 507static CallableMaterializationKind508getCallableMaterialization(const MatchFinder::MatchResult &Result) {509 const auto *CallableExpr = Result.Nodes.getNodeAs<Expr>("ref");510 511 const auto *NoTemporaries = ignoreTemporariesAndPointers(CallableExpr);512 513 const auto *CE = dyn_cast<CXXConstructExpr>(NoTemporaries);514 const auto *FC = dyn_cast<CXXFunctionalCastExpr>(NoTemporaries);515 if ((isa<CallExpr>(NoTemporaries)) || (CE && (CE->getNumArgs() > 0)) ||516 (FC && (FC->getCastKind() == CK_ConstructorConversion)))517 // CE is something that looks like a call, with arguments - either518 // a function call or a constructor invocation.519 return CMK_CallExpression;520 521 if (isa<CXXFunctionalCastExpr>(NoTemporaries) || CE)522 return CMK_Function;523 524 if (const auto *DRE = dyn_cast<DeclRefExpr>(NoTemporaries)) {525 if (isa<FunctionDecl>(DRE->getDecl()))526 return CMK_Function;527 if (isa<VarDecl>(DRE->getDecl()))528 return CMK_VariableRef;529 }530 531 return CMK_Other;532}533 534static LambdaProperties535getLambdaProperties(const MatchFinder::MatchResult &Result) {536 const auto *CalleeExpr = Result.Nodes.getNodeAs<Expr>("ref");537 538 LambdaProperties LP;539 540 const auto *Bind = Result.Nodes.getNodeAs<CallExpr>("bind");541 const auto *Decl = cast<FunctionDecl>(Bind->getCalleeDecl());542 const auto *NS = cast<NamespaceDecl>(Decl->getEnclosingNamespaceContext());543 while (NS->isInlineNamespace())544 NS = cast<NamespaceDecl>(NS->getDeclContext());545 LP.BindNamespace = NS->getName();546 547 LP.Callable.Type = getCallableType(Result);548 LP.Callable.Materialization = getCallableMaterialization(Result);549 LP.Callable.Decl =550 getCallMethodDecl(Result, LP.Callable.Type, LP.Callable.Materialization);551 if (LP.Callable.Decl)552 if (const Type *ReturnType =553 LP.Callable.Decl->getReturnType().getCanonicalType().getTypePtr())554 LP.Callable.DoesReturn = !ReturnType->isVoidType();555 LP.Callable.SourceTokens = getSourceTextForExpr(Result, CalleeExpr);556 if (LP.Callable.Materialization == CMK_VariableRef) {557 LP.Callable.CE = CE_Var;558 LP.Callable.CM = CM_ByValue;559 LP.Callable.UsageIdentifier =560 std::string(getSourceTextForExpr(Result, CalleeExpr));561 LP.Callable.CaptureIdentifier = std::string(562 getSourceTextForExpr(Result, ignoreTemporariesAndPointers(CalleeExpr)));563 } else if (LP.Callable.Materialization == CMK_CallExpression) {564 LP.Callable.CE = CE_InitExpression;565 LP.Callable.CM = CM_ByValue;566 LP.Callable.UsageIdentifier = "Func";567 LP.Callable.CaptureIdentifier = "Func";568 LP.Callable.CaptureInitializer = getSourceTextForExpr(Result, CalleeExpr);569 }570 571 LP.BindArguments = buildBindArguments(Result, LP.Callable);572 573 LP.IsFixitSupported = isFixitSupported(LP.Callable, LP.BindArguments);574 575 return LP;576}577 578static bool emitCapture(llvm::StringSet<> &CaptureSet, StringRef Delimiter,579 CaptureMode CM, CaptureExpr CE, StringRef Identifier,580 StringRef InitExpression, raw_ostream &Stream) {581 if (CM == CM_None)582 return false;583 584 // This capture has already been emitted.585 if (CaptureSet.contains(Identifier))586 return false;587 588 Stream << Delimiter;589 590 if (CM == CM_ByRef)591 Stream << "&";592 Stream << Identifier;593 if (CE == CE_InitExpression)594 Stream << " = " << InitExpression;595 596 CaptureSet.insert(Identifier);597 return true;598}599 600static void emitCaptureList(const LambdaProperties &LP,601 const MatchFinder::MatchResult &Result,602 raw_ostream &Stream) {603 llvm::StringSet<> CaptureSet;604 bool AnyCapturesEmitted = false;605 606 AnyCapturesEmitted = emitCapture(607 CaptureSet, "", LP.Callable.CM, LP.Callable.CE,608 LP.Callable.CaptureIdentifier, LP.Callable.CaptureInitializer, Stream);609 610 for (const BindArgument &B : LP.BindArguments) {611 if (B.CM == CM_None || !B.IsUsed)612 continue;613 614 const StringRef Delimiter = AnyCapturesEmitted ? ", " : "";615 616 if (emitCapture(CaptureSet, Delimiter, B.CM, B.CE, B.CaptureIdentifier,617 B.SourceTokens, Stream))618 AnyCapturesEmitted = true;619 }620}621 622static ArrayRef<BindArgument>623getForwardedArgumentList(const LambdaProperties &P) {624 ArrayRef<BindArgument> Args = ArrayRef(P.BindArguments);625 if (P.Callable.Type != CT_MemberFunction)626 return Args;627 628 return Args.drop_front();629}630AvoidBindCheck::AvoidBindCheck(StringRef Name, ClangTidyContext *Context)631 : ClangTidyCheck(Name, Context),632 PermissiveParameterList(Options.get("PermissiveParameterList", false)) {}633 634void AvoidBindCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {635 Options.store(Opts, "PermissiveParameterList", PermissiveParameterList);636}637 638void AvoidBindCheck::registerMatchers(MatchFinder *Finder) {639 Finder->addMatcher(640 callExpr(641 callee(namedDecl(hasAnyName("::boost::bind", "::std::bind"))),642 hasArgument(643 0, anyOf(expr(hasType(memberPointerType())).bind("ref"),644 expr(hasParent(materializeTemporaryExpr().bind("ref"))),645 expr().bind("ref"))))646 .bind("bind"),647 this);648}649 650void AvoidBindCheck::check(const MatchFinder::MatchResult &Result) {651 const auto *MatchedDecl = Result.Nodes.getNodeAs<CallExpr>("bind");652 653 LambdaProperties LP = getLambdaProperties(Result);654 auto Diag =655 diag(MatchedDecl->getBeginLoc(),656 formatv("prefer a lambda to {0}::bind", LP.BindNamespace).str());657 if (!LP.IsFixitSupported)658 return;659 660 const auto *Ref = Result.Nodes.getNodeAs<Expr>("ref");661 662 std::string Buffer;663 llvm::raw_string_ostream Stream(Buffer);664 665 Stream << "[";666 emitCaptureList(LP, Result, Stream);667 Stream << "]";668 669 const ArrayRef<BindArgument> FunctionCallArgs = ArrayRef(LP.BindArguments);670 671 addPlaceholderArgs(LP, Stream, PermissiveParameterList);672 673 Stream << " { ";674 675 if (LP.Callable.DoesReturn) {676 Stream << "return ";677 }678 679 if (LP.Callable.Type == CT_Function) {680 StringRef SourceTokens = LP.Callable.SourceTokens;681 SourceTokens.consume_front("&");682 Stream << SourceTokens;683 } else if (LP.Callable.Type == CT_MemberFunction) {684 const auto *MethodDecl = dyn_cast<CXXMethodDecl>(LP.Callable.Decl);685 const BindArgument &ObjPtr = FunctionCallArgs.front();686 687 if (MethodDecl->getOverloadedOperator() == OO_Call) {688 Stream << "(*" << ObjPtr.UsageIdentifier << ')';689 } else {690 if (!isa<CXXThisExpr>(ignoreTemporariesAndPointers(ObjPtr.E))) {691 Stream << ObjPtr.UsageIdentifier;692 Stream << "->";693 }694 Stream << MethodDecl->getNameAsString();695 }696 } else {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 [[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 clang::tidy::modernize722