brintos

brintos / llvm-project-archived public Read only

0
0
Text · 23.2 KiB · 7663f37 Raw
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