forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			662 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			662 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C++
		
	
	
	
//===--- AvoidBindCheck.cpp - clang-tidy-----------------------------------===//
 | 
						|
//
 | 
						|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | 
						|
// See https://llvm.org/LICENSE.txt for license information.
 | 
						|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | 
						|
//
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
 | 
						|
#include "AvoidBindCheck.h"
 | 
						|
#include "clang/AST/ASTContext.h"
 | 
						|
#include "clang/ASTMatchers/ASTMatchFinder.h"
 | 
						|
#include "clang/Basic/LLVM.h"
 | 
						|
#include "clang/Basic/LangOptions.h"
 | 
						|
#include "clang/Basic/SourceLocation.h"
 | 
						|
#include "clang/Lex/Lexer.h"
 | 
						|
#include "llvm/ADT/ArrayRef.h"
 | 
						|
#include "llvm/ADT/STLExtras.h"
 | 
						|
#include "llvm/ADT/SmallSet.h"
 | 
						|
#include "llvm/ADT/SmallVector.h"
 | 
						|
#include "llvm/ADT/StringRef.h"
 | 
						|
#include "llvm/Support/Casting.h"
 | 
						|
#include "llvm/Support/FormatVariadic.h"
 | 
						|
#include "llvm/Support/Regex.h"
 | 
						|
#include "llvm/Support/raw_ostream.h"
 | 
						|
#include <algorithm>
 | 
						|
#include <cstddef>
 | 
						|
#include <string>
 | 
						|
 | 
						|
using namespace clang::ast_matchers;
 | 
						|
 | 
						|
namespace clang {
 | 
						|
namespace tidy {
 | 
						|
namespace modernize {
 | 
						|
 | 
						|
namespace {
 | 
						|
 | 
						|
enum BindArgumentKind { BK_Temporary, BK_Placeholder, BK_CallExpr, BK_Other };
 | 
						|
enum CaptureMode { CM_None, CM_ByRef, CM_ByValue, CM_InitExpression };
 | 
						|
 | 
						|
enum CallableType {
 | 
						|
  CT_Other,          // unknown
 | 
						|
  CT_Function,       // global or static function
 | 
						|
  CT_MemberFunction, // member function with implicit this
 | 
						|
  CT_Object,         // object with operator()
 | 
						|
};
 | 
						|
 | 
						|
enum CallableMaterializationKind {
 | 
						|
  CMK_Other,       // unknown
 | 
						|
  CMK_Function,    // callable is the name of a member or non-member function.
 | 
						|
  CMK_VariableRef, // callable is a simple expression involving a global or
 | 
						|
                   // local variable.
 | 
						|
  CMK_CallExpression, // callable is obtained as the result of a call expression
 | 
						|
};
 | 
						|
 | 
						|
struct BindArgument {
 | 
						|
  // A rough classification of the type of expression this argument was.
 | 
						|
  BindArgumentKind Kind = BK_Other;
 | 
						|
 | 
						|
  // If this argument required a capture, a value indicating how it was
 | 
						|
  // captured.
 | 
						|
  CaptureMode CM = CM_None;
 | 
						|
 | 
						|
  // The exact spelling of this argument in the source code.
 | 
						|
  StringRef SourceTokens;
 | 
						|
 | 
						|
  // The identifier of the variable within the capture list.  This may be
 | 
						|
  // different from UsageIdentifier for example in the expression *d, where the
 | 
						|
  // variable is captured as d, but referred to as *d.
 | 
						|
  std::string CaptureIdentifier;
 | 
						|
 | 
						|
  // If this is a placeholder or capture init expression, contains the tokens
 | 
						|
  // used to refer to this parameter from within the body of the lambda.
 | 
						|
  std::string UsageIdentifier;
 | 
						|
 | 
						|
  // If Kind == BK_Placeholder, the index of the placeholder.
 | 
						|
  size_t PlaceHolderIndex = 0;
 | 
						|
 | 
						|
  // True if the argument is used inside the lambda, false otherwise.
 | 
						|
  bool IsUsed = false;
 | 
						|
 | 
						|
  // The actual Expr object representing this expression.
 | 
						|
  const Expr *E = nullptr;
 | 
						|
};
 | 
						|
 | 
						|
struct CallableInfo {
 | 
						|
  CallableType Type = CT_Other;
 | 
						|
  CallableMaterializationKind Materialization = CMK_Other;
 | 
						|
  CaptureMode CM = CM_None;
 | 
						|
  StringRef SourceTokens;
 | 
						|
  std::string CaptureIdentifier;
 | 
						|
  std::string UsageIdentifier;
 | 
						|
  StringRef CaptureInitializer;
 | 
						|
  const FunctionDecl *Decl = nullptr;
 | 
						|
};
 | 
						|
 | 
						|
struct LambdaProperties {
 | 
						|
  CallableInfo Callable;
 | 
						|
  SmallVector<BindArgument, 4> BindArguments;
 | 
						|
  StringRef BindNamespace;
 | 
						|
  bool IsFixitSupported = false;
 | 
						|
};
 | 
						|
 | 
						|
} // end namespace
 | 
						|
 | 
						|
static const Expr *ignoreTemporariesAndPointers(const Expr *E) {
 | 
						|
  if (const auto *T = dyn_cast<UnaryOperator>(E))
 | 
						|
    return ignoreTemporariesAndPointers(T->getSubExpr());
 | 
						|
 | 
						|
  const Expr *F = E->IgnoreImplicit();
 | 
						|
  if (E != F)
 | 
						|
    return ignoreTemporariesAndPointers(F);
 | 
						|
 | 
						|
  return E;
 | 
						|
}
 | 
						|
 | 
						|
static const Expr *ignoreTemporariesAndConstructors(const Expr *E) {
 | 
						|
  if (const auto *T = dyn_cast<CXXConstructExpr>(E))
 | 
						|
    return ignoreTemporariesAndConstructors(T->getArg(0));
 | 
						|
 | 
						|
  const Expr *F = E->IgnoreImplicit();
 | 
						|
  if (E != F)
 | 
						|
    return ignoreTemporariesAndPointers(F);
 | 
						|
 | 
						|
  return E;
 | 
						|
}
 | 
						|
 | 
						|
static StringRef getSourceTextForExpr(const MatchFinder::MatchResult &Result,
 | 
						|
                                      const Expr *E) {
 | 
						|
  return Lexer::getSourceText(
 | 
						|
      CharSourceRange::getTokenRange(E->getBeginLoc(), E->getEndLoc()),
 | 
						|
      *Result.SourceManager, Result.Context->getLangOpts());
 | 
						|
}
 | 
						|
 | 
						|
static bool isCallExprNamed(const Expr *E, StringRef Name) {
 | 
						|
  const auto *CE = dyn_cast<CallExpr>(E->IgnoreImplicit());
 | 
						|
  if (!CE)
 | 
						|
    return false;
 | 
						|
  const auto *ND = dyn_cast<NamedDecl>(CE->getCalleeDecl());
 | 
						|
  if (!ND)
 | 
						|
    return false;
 | 
						|
  return ND->getQualifiedNameAsString() == Name;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
initializeBindArgumentForCallExpr(const MatchFinder::MatchResult &Result,
 | 
						|
                                  BindArgument &B, const CallExpr *CE,
 | 
						|
                                  unsigned &CaptureIndex) {
 | 
						|
  // std::ref(x) means to capture x by reference.
 | 
						|
  if (isCallExprNamed(CE, "boost::ref") || isCallExprNamed(CE, "std::ref")) {
 | 
						|
    B.Kind = BK_Other;
 | 
						|
    B.CM = CM_ByRef;
 | 
						|
    B.UsageIdentifier =
 | 
						|
        std::string(getSourceTextForExpr(Result, CE->getArg(0)));
 | 
						|
  } else {
 | 
						|
    B.Kind = BK_CallExpr;
 | 
						|
    B.CM = CM_InitExpression;
 | 
						|
    B.UsageIdentifier = "capture" + llvm::utostr(CaptureIndex++);
 | 
						|
  }
 | 
						|
  B.CaptureIdentifier = B.UsageIdentifier;
 | 
						|
}
 | 
						|
 | 
						|
static bool anyDescendantIsLocal(const Stmt *Statement) {
 | 
						|
  if (const auto *DeclRef = dyn_cast<DeclRefExpr>(Statement)) {
 | 
						|
    const ValueDecl *Decl = DeclRef->getDecl();
 | 
						|
    if (const auto *Var = dyn_cast_or_null<VarDecl>(Decl)) {
 | 
						|
      if (Var->isLocalVarDeclOrParm())
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
  } else if (isa<CXXThisExpr>(Statement))
 | 
						|
    return true;
 | 
						|
 | 
						|
  return any_of(Statement->children(), anyDescendantIsLocal);
 | 
						|
}
 | 
						|
 | 
						|
static bool tryCaptureAsLocalVariable(const MatchFinder::MatchResult &Result,
 | 
						|
                                      BindArgument &B, const Expr *E) {
 | 
						|
  if (const auto *BTE = dyn_cast<CXXBindTemporaryExpr>(E)) {
 | 
						|
    if (const auto *CE = dyn_cast<CXXConstructExpr>(BTE->getSubExpr()))
 | 
						|
      return tryCaptureAsLocalVariable(Result, B, CE->getArg(0));
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  const auto *DRE = dyn_cast<DeclRefExpr>(E->IgnoreImplicit());
 | 
						|
  if (!DRE)
 | 
						|
    return false;
 | 
						|
 | 
						|
  const auto *VD = dyn_cast<VarDecl>(DRE->getDecl());
 | 
						|
  if (!VD || !VD->isLocalVarDeclOrParm())
 | 
						|
    return false;
 | 
						|
 | 
						|
  B.CM = CM_ByValue;
 | 
						|
  B.UsageIdentifier = std::string(getSourceTextForExpr(Result, E));
 | 
						|
  B.CaptureIdentifier = B.UsageIdentifier;
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
static bool tryCaptureAsMemberVariable(const MatchFinder::MatchResult &Result,
 | 
						|
                                       BindArgument &B, const Expr *E) {
 | 
						|
  if (const auto *BTE = dyn_cast<CXXBindTemporaryExpr>(E)) {
 | 
						|
    if (const auto *CE = dyn_cast<CXXConstructExpr>(BTE->getSubExpr()))
 | 
						|
      return tryCaptureAsMemberVariable(Result, B, CE->getArg(0));
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  E = E->IgnoreImplicit();
 | 
						|
  if (isa<CXXThisExpr>(E)) {
 | 
						|
    B.CM = CM_ByValue;
 | 
						|
    B.UsageIdentifier = std::string(getSourceTextForExpr(Result, E));
 | 
						|
    B.CaptureIdentifier = "this";
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
 | 
						|
  const auto *ME = dyn_cast<MemberExpr>(E);
 | 
						|
  if (!ME)
 | 
						|
    return false;
 | 
						|
 | 
						|
  if (!ME->isLValue() || !isa<FieldDecl>(ME->getMemberDecl()))
 | 
						|
    return false;
 | 
						|
 | 
						|
  B.CM = CM_ByValue;
 | 
						|
  B.UsageIdentifier = std::string(getSourceTextForExpr(Result, E));
 | 
						|
  B.CaptureIdentifier = "this";
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
static SmallVector<BindArgument, 4>
 | 
						|
buildBindArguments(const MatchFinder::MatchResult &Result,
 | 
						|
                   const CallableInfo &Callable) {
 | 
						|
  SmallVector<BindArgument, 4> BindArguments;
 | 
						|
  llvm::Regex MatchPlaceholder("^_([0-9]+)$");
 | 
						|
 | 
						|
  const auto *BindCall = Result.Nodes.getNodeAs<CallExpr>("bind");
 | 
						|
 | 
						|
  // Start at index 1 as first argument to bind is the function name.
 | 
						|
  unsigned CaptureIndex = 0;
 | 
						|
  for (size_t I = 1, ArgCount = BindCall->getNumArgs(); I < ArgCount; ++I) {
 | 
						|
 | 
						|
    const Expr *E = BindCall->getArg(I);
 | 
						|
    BindArgument &B = BindArguments.emplace_back();
 | 
						|
 | 
						|
    size_t ArgIndex = I - 1;
 | 
						|
    if (Callable.Type == CT_MemberFunction)
 | 
						|
      --ArgIndex;
 | 
						|
 | 
						|
    bool IsObjectPtr = (I == 1 && Callable.Type == CT_MemberFunction);
 | 
						|
    B.E = E;
 | 
						|
    B.SourceTokens = getSourceTextForExpr(Result, E);
 | 
						|
 | 
						|
    if (!Callable.Decl || ArgIndex < Callable.Decl->getNumParams() ||
 | 
						|
        IsObjectPtr)
 | 
						|
      B.IsUsed = true;
 | 
						|
 | 
						|
    SmallVector<StringRef, 2> Matches;
 | 
						|
    if (MatchPlaceholder.match(B.SourceTokens, &Matches)) {
 | 
						|
      B.Kind = BK_Placeholder;
 | 
						|
      B.PlaceHolderIndex = std::stoi(std::string(Matches[1]));
 | 
						|
      B.UsageIdentifier = "PH" + llvm::utostr(B.PlaceHolderIndex);
 | 
						|
      B.CaptureIdentifier = B.UsageIdentifier;
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    if (const auto *CE =
 | 
						|
            dyn_cast<CallExpr>(ignoreTemporariesAndConstructors(E))) {
 | 
						|
      initializeBindArgumentForCallExpr(Result, B, CE, CaptureIndex);
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    if (tryCaptureAsLocalVariable(Result, B, B.E) ||
 | 
						|
        tryCaptureAsMemberVariable(Result, B, B.E))
 | 
						|
      continue;
 | 
						|
 | 
						|
    // If it's not something we recognize, capture it by init expression to be
 | 
						|
    // safe.
 | 
						|
    B.Kind = BK_Other;
 | 
						|
    if (IsObjectPtr) {
 | 
						|
      B.CM = CM_InitExpression;
 | 
						|
      B.UsageIdentifier = "ObjectPtr";
 | 
						|
      B.CaptureIdentifier = B.UsageIdentifier;
 | 
						|
    } else if (anyDescendantIsLocal(B.E)) {
 | 
						|
      B.CM = CM_InitExpression;
 | 
						|
      B.CaptureIdentifier = "capture" + llvm::utostr(CaptureIndex++);
 | 
						|
      B.UsageIdentifier = B.CaptureIdentifier;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return BindArguments;
 | 
						|
}
 | 
						|
 | 
						|
static int findPositionOfPlaceholderUse(ArrayRef<BindArgument> Args,
 | 
						|
                                        size_t PlaceholderIndex) {
 | 
						|
  for (size_t I = 0; I < Args.size(); ++I)
 | 
						|
    if (Args[I].PlaceHolderIndex == PlaceholderIndex)
 | 
						|
      return I;
 | 
						|
 | 
						|
  return -1;
 | 
						|
}
 | 
						|
 | 
						|
static void addPlaceholderArgs(const LambdaProperties &LP,
 | 
						|
                               llvm::raw_ostream &Stream,
 | 
						|
                               bool PermissiveParameterList) {
 | 
						|
 | 
						|
  ArrayRef<BindArgument> Args = LP.BindArguments;
 | 
						|
 | 
						|
  auto MaxPlaceholderIt =
 | 
						|
      std::max_element(Args.begin(), Args.end(),
 | 
						|
                       [](const BindArgument &B1, const BindArgument &B2) {
 | 
						|
                         return B1.PlaceHolderIndex < B2.PlaceHolderIndex;
 | 
						|
                       });
 | 
						|
 | 
						|
  // Placeholders (if present) have index 1 or greater.
 | 
						|
  if (!PermissiveParameterList && (MaxPlaceholderIt == Args.end() ||
 | 
						|
                                   MaxPlaceholderIt->PlaceHolderIndex == 0))
 | 
						|
    return;
 | 
						|
 | 
						|
  size_t PlaceholderCount = MaxPlaceholderIt->PlaceHolderIndex;
 | 
						|
  Stream << "(";
 | 
						|
  StringRef Delimiter = "";
 | 
						|
  for (size_t I = 1; I <= PlaceholderCount; ++I) {
 | 
						|
    Stream << Delimiter << "auto &&";
 | 
						|
 | 
						|
    int ArgIndex = findPositionOfPlaceholderUse(Args, I);
 | 
						|
 | 
						|
    if (ArgIndex != -1 && Args[ArgIndex].IsUsed)
 | 
						|
      Stream << " " << Args[ArgIndex].UsageIdentifier;
 | 
						|
    Delimiter = ", ";
 | 
						|
  }
 | 
						|
  if (PermissiveParameterList)
 | 
						|
    Stream << Delimiter << "auto && ...";
 | 
						|
  Stream << ")";
 | 
						|
}
 | 
						|
 | 
						|
static void addFunctionCallArgs(ArrayRef<BindArgument> Args,
 | 
						|
                                llvm::raw_ostream &Stream) {
 | 
						|
  StringRef Delimiter = "";
 | 
						|
 | 
						|
  for (int I = 0, Size = Args.size(); I < Size; ++I) {
 | 
						|
    const BindArgument &B = Args[I];
 | 
						|
 | 
						|
    Stream << Delimiter;
 | 
						|
 | 
						|
    if (B.Kind == BK_Placeholder || B.CM != CM_None)
 | 
						|
      Stream << B.UsageIdentifier;
 | 
						|
    else if (B.CM == CM_None)
 | 
						|
      Stream << B.SourceTokens;
 | 
						|
 | 
						|
    Delimiter = ", ";
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static bool isPlaceHolderIndexRepeated(const ArrayRef<BindArgument> Args) {
 | 
						|
  llvm::SmallSet<size_t, 4> PlaceHolderIndices;
 | 
						|
  for (const BindArgument &B : Args) {
 | 
						|
    if (B.PlaceHolderIndex) {
 | 
						|
      if (!PlaceHolderIndices.insert(B.PlaceHolderIndex).second)
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
static std::vector<const CXXMethodDecl *>
 | 
						|
findCandidateCallOperators(const CXXRecordDecl *RecordDecl, size_t NumArgs) {
 | 
						|
  std::vector<const CXXMethodDecl *> Candidates;
 | 
						|
 | 
						|
  for (const clang::CXXMethodDecl *Method : RecordDecl->methods()) {
 | 
						|
    OverloadedOperatorKind OOK = Method->getOverloadedOperator();
 | 
						|
 | 
						|
    if (OOK != OverloadedOperatorKind::OO_Call)
 | 
						|
      continue;
 | 
						|
 | 
						|
    if (Method->getNumParams() > NumArgs)
 | 
						|
      continue;
 | 
						|
 | 
						|
    Candidates.push_back(Method);
 | 
						|
  }
 | 
						|
 | 
						|
  return Candidates;
 | 
						|
}
 | 
						|
 | 
						|
static bool isFixitSupported(const CallableInfo &Callee,
 | 
						|
                             ArrayRef<BindArgument> Args) {
 | 
						|
  // Do not attempt to create fixits for nested std::bind or std::ref.
 | 
						|
  // Supporting nested std::bind will be more difficult due to placeholder
 | 
						|
  // sharing between outer and inner std::bind invocations, and std::ref
 | 
						|
  // requires us to capture some parameters by reference instead of by value.
 | 
						|
  if (any_of(Args, [](const BindArgument &B) {
 | 
						|
        return isCallExprNamed(B.E, "boost::bind") ||
 | 
						|
               isCallExprNamed(B.E, "std::bind");
 | 
						|
      })) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  // Do not attempt to create fixits when placeholders are reused.
 | 
						|
  // Unused placeholders are supported by requiring C++14 generic lambdas.
 | 
						|
  // FIXME: Support this case by deducing the common type.
 | 
						|
  if (isPlaceHolderIndexRepeated(Args))
 | 
						|
    return false;
 | 
						|
 | 
						|
  // If we can't determine the Decl being used, don't offer a fixit.
 | 
						|
  if (!Callee.Decl)
 | 
						|
    return false;
 | 
						|
 | 
						|
  if (Callee.Type == CT_Other || Callee.Materialization == CMK_Other)
 | 
						|
    return false;
 | 
						|
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
const FunctionDecl *getCallOperator(const CXXRecordDecl *Callable,
 | 
						|
                                    size_t NumArgs) {
 | 
						|
  std::vector<const CXXMethodDecl *> Candidates =
 | 
						|
      findCandidateCallOperators(Callable, NumArgs);
 | 
						|
  if (Candidates.size() != 1)
 | 
						|
    return nullptr;
 | 
						|
 | 
						|
  return Candidates.front();
 | 
						|
}
 | 
						|
 | 
						|
const FunctionDecl *
 | 
						|
getCallMethodDecl(const MatchFinder::MatchResult &Result, CallableType Type,
 | 
						|
                  CallableMaterializationKind Materialization) {
 | 
						|
 | 
						|
  const Expr *Callee = Result.Nodes.getNodeAs<Expr>("ref");
 | 
						|
  const Expr *CallExpression = ignoreTemporariesAndPointers(Callee);
 | 
						|
 | 
						|
  if (Type == CT_Object) {
 | 
						|
    const auto *BindCall = Result.Nodes.getNodeAs<CallExpr>("bind");
 | 
						|
    size_t NumArgs = BindCall->getNumArgs() - 1;
 | 
						|
    return getCallOperator(Callee->getType()->getAsCXXRecordDecl(), NumArgs);
 | 
						|
  }
 | 
						|
 | 
						|
  if (Materialization == CMK_Function) {
 | 
						|
    if (const auto *DRE = dyn_cast<DeclRefExpr>(CallExpression))
 | 
						|
      return dyn_cast<FunctionDecl>(DRE->getDecl());
 | 
						|
  }
 | 
						|
 | 
						|
  // Maybe this is an indirect call through a function pointer or something
 | 
						|
  // where we can't determine the exact decl.
 | 
						|
  return nullptr;
 | 
						|
}
 | 
						|
 | 
						|
static CallableType getCallableType(const MatchFinder::MatchResult &Result) {
 | 
						|
  const auto *CallableExpr = Result.Nodes.getNodeAs<Expr>("ref");
 | 
						|
 | 
						|
  QualType QT = CallableExpr->getType();
 | 
						|
  if (QT->isMemberFunctionPointerType())
 | 
						|
    return CT_MemberFunction;
 | 
						|
 | 
						|
  if (QT->isFunctionPointerType() || QT->isFunctionReferenceType() ||
 | 
						|
      QT->isFunctionType())
 | 
						|
    return CT_Function;
 | 
						|
 | 
						|
  if (QT->isRecordType()) {
 | 
						|
    const CXXRecordDecl *Decl = QT->getAsCXXRecordDecl();
 | 
						|
    if (!Decl)
 | 
						|
      return CT_Other;
 | 
						|
 | 
						|
    return CT_Object;
 | 
						|
  }
 | 
						|
 | 
						|
  return CT_Other;
 | 
						|
}
 | 
						|
 | 
						|
static CallableMaterializationKind
 | 
						|
getCallableMaterialization(const MatchFinder::MatchResult &Result) {
 | 
						|
  const auto *CallableExpr = Result.Nodes.getNodeAs<Expr>("ref");
 | 
						|
 | 
						|
  const auto *NoTemporaries = ignoreTemporariesAndPointers(CallableExpr);
 | 
						|
 | 
						|
  if (isa<CallExpr>(NoTemporaries))
 | 
						|
    return CMK_CallExpression;
 | 
						|
 | 
						|
  if (isa<CXXFunctionalCastExpr>(NoTemporaries) ||
 | 
						|
      isa<CXXConstructExpr>(NoTemporaries))
 | 
						|
    return CMK_Function;
 | 
						|
 | 
						|
  if (const auto *DRE = dyn_cast<DeclRefExpr>(NoTemporaries)) {
 | 
						|
    if (isa<FunctionDecl>(DRE->getDecl()))
 | 
						|
      return CMK_Function;
 | 
						|
    if (isa<VarDecl>(DRE->getDecl()))
 | 
						|
      return CMK_VariableRef;
 | 
						|
  }
 | 
						|
 | 
						|
  return CMK_Other;
 | 
						|
}
 | 
						|
 | 
						|
static LambdaProperties
 | 
						|
getLambdaProperties(const MatchFinder::MatchResult &Result) {
 | 
						|
  const auto *CalleeExpr = Result.Nodes.getNodeAs<Expr>("ref");
 | 
						|
 | 
						|
  LambdaProperties LP;
 | 
						|
 | 
						|
  const auto *Bind = Result.Nodes.getNodeAs<CallExpr>("bind");
 | 
						|
  const auto *Decl = dyn_cast<FunctionDecl>(Bind->getCalleeDecl());
 | 
						|
  const auto *NS =
 | 
						|
      dyn_cast<NamespaceDecl>(Decl->getEnclosingNamespaceContext());
 | 
						|
  while (NS->isInlineNamespace())
 | 
						|
    NS = dyn_cast<NamespaceDecl>(NS->getDeclContext());
 | 
						|
  LP.BindNamespace = NS->getName();
 | 
						|
 | 
						|
  LP.Callable.Type = getCallableType(Result);
 | 
						|
  LP.Callable.Materialization = getCallableMaterialization(Result);
 | 
						|
  LP.Callable.Decl =
 | 
						|
      getCallMethodDecl(Result, LP.Callable.Type, LP.Callable.Materialization);
 | 
						|
  LP.Callable.SourceTokens = getSourceTextForExpr(Result, CalleeExpr);
 | 
						|
  if (LP.Callable.Materialization == CMK_VariableRef) {
 | 
						|
    LP.Callable.CM = CM_ByValue;
 | 
						|
    LP.Callable.UsageIdentifier =
 | 
						|
        std::string(getSourceTextForExpr(Result, CalleeExpr));
 | 
						|
    LP.Callable.CaptureIdentifier = std::string(
 | 
						|
        getSourceTextForExpr(Result, ignoreTemporariesAndPointers(CalleeExpr)));
 | 
						|
  } else if (LP.Callable.Materialization == CMK_CallExpression) {
 | 
						|
    LP.Callable.CM = CM_InitExpression;
 | 
						|
    LP.Callable.UsageIdentifier = "Func";
 | 
						|
    LP.Callable.CaptureIdentifier = "Func";
 | 
						|
    LP.Callable.CaptureInitializer = getSourceTextForExpr(Result, CalleeExpr);
 | 
						|
  }
 | 
						|
 | 
						|
  LP.BindArguments = buildBindArguments(Result, LP.Callable);
 | 
						|
 | 
						|
  LP.IsFixitSupported = isFixitSupported(LP.Callable, LP.BindArguments);
 | 
						|
 | 
						|
  return LP;
 | 
						|
}
 | 
						|
 | 
						|
static bool emitCapture(llvm::StringSet<> &CaptureSet, StringRef Delimiter,
 | 
						|
                        CaptureMode CM, StringRef Identifier,
 | 
						|
                        StringRef InitExpression, raw_ostream &Stream) {
 | 
						|
  if (CM == CM_None)
 | 
						|
    return false;
 | 
						|
 | 
						|
  // This capture has already been emitted.
 | 
						|
  if (CaptureSet.count(Identifier) != 0)
 | 
						|
    return false;
 | 
						|
 | 
						|
  Stream << Delimiter;
 | 
						|
 | 
						|
  if (CM == CM_ByRef)
 | 
						|
    Stream << "&";
 | 
						|
  Stream << Identifier;
 | 
						|
  if (CM == CM_InitExpression)
 | 
						|
    Stream << " = " << InitExpression;
 | 
						|
 | 
						|
  CaptureSet.insert(Identifier);
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
static void emitCaptureList(const LambdaProperties &LP,
 | 
						|
                            const MatchFinder::MatchResult &Result,
 | 
						|
                            raw_ostream &Stream) {
 | 
						|
  llvm::StringSet<> CaptureSet;
 | 
						|
  bool AnyCapturesEmitted = false;
 | 
						|
 | 
						|
  AnyCapturesEmitted = emitCapture(CaptureSet, "", LP.Callable.CM,
 | 
						|
                                   LP.Callable.CaptureIdentifier,
 | 
						|
                                   LP.Callable.CaptureInitializer, Stream);
 | 
						|
 | 
						|
  for (const BindArgument &B : LP.BindArguments) {
 | 
						|
    if (B.CM == CM_None || !B.IsUsed)
 | 
						|
      continue;
 | 
						|
 | 
						|
    StringRef Delimiter = AnyCapturesEmitted ? ", " : "";
 | 
						|
 | 
						|
    if (emitCapture(CaptureSet, Delimiter, B.CM, B.CaptureIdentifier,
 | 
						|
                    B.SourceTokens, Stream))
 | 
						|
      AnyCapturesEmitted = true;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static ArrayRef<BindArgument>
 | 
						|
getForwardedArgumentList(const LambdaProperties &P) {
 | 
						|
  ArrayRef<BindArgument> Args = makeArrayRef(P.BindArguments);
 | 
						|
  if (P.Callable.Type != CT_MemberFunction)
 | 
						|
    return Args;
 | 
						|
 | 
						|
  return Args.drop_front();
 | 
						|
}
 | 
						|
AvoidBindCheck::AvoidBindCheck(StringRef Name, ClangTidyContext *Context)
 | 
						|
    : ClangTidyCheck(Name, Context),
 | 
						|
      PermissiveParameterList(Options.get("PermissiveParameterList", 0) != 0) {}
 | 
						|
 | 
						|
void AvoidBindCheck::registerMatchers(MatchFinder *Finder) {
 | 
						|
  Finder->addMatcher(
 | 
						|
      callExpr(
 | 
						|
          callee(namedDecl(
 | 
						|
              anyOf(hasName("::boost::bind"), hasName("::std::bind")))),
 | 
						|
          hasArgument(
 | 
						|
              0, anyOf(expr(hasType(memberPointerType())).bind("ref"),
 | 
						|
                       expr(hasParent(materializeTemporaryExpr().bind("ref"))),
 | 
						|
                       expr().bind("ref"))))
 | 
						|
          .bind("bind"),
 | 
						|
      this);
 | 
						|
}
 | 
						|
 | 
						|
void AvoidBindCheck::check(const MatchFinder::MatchResult &Result) {
 | 
						|
  const auto *MatchedDecl = Result.Nodes.getNodeAs<CallExpr>("bind");
 | 
						|
 | 
						|
  LambdaProperties LP = getLambdaProperties(Result);
 | 
						|
  auto Diag =
 | 
						|
      diag(MatchedDecl->getBeginLoc(),
 | 
						|
           formatv("prefer a lambda to {0}::bind", LP.BindNamespace).str());
 | 
						|
  if (!LP.IsFixitSupported)
 | 
						|
    return;
 | 
						|
 | 
						|
  const auto *Ref = Result.Nodes.getNodeAs<Expr>("ref");
 | 
						|
 | 
						|
  std::string Buffer;
 | 
						|
  llvm::raw_string_ostream Stream(Buffer);
 | 
						|
 | 
						|
  Stream << "[";
 | 
						|
  emitCaptureList(LP, Result, Stream);
 | 
						|
  Stream << "]";
 | 
						|
 | 
						|
  ArrayRef<BindArgument> FunctionCallArgs = makeArrayRef(LP.BindArguments);
 | 
						|
 | 
						|
  addPlaceholderArgs(LP, Stream, PermissiveParameterList);
 | 
						|
 | 
						|
  if (LP.Callable.Type == CT_Function) {
 | 
						|
    StringRef SourceTokens = LP.Callable.SourceTokens;
 | 
						|
    SourceTokens.consume_front("&");
 | 
						|
    Stream << " { return " << SourceTokens;
 | 
						|
  } else if (LP.Callable.Type == CT_MemberFunction) {
 | 
						|
    const auto *MethodDecl = dyn_cast<CXXMethodDecl>(LP.Callable.Decl);
 | 
						|
    const BindArgument &ObjPtr = FunctionCallArgs.front();
 | 
						|
 | 
						|
    Stream << " { ";
 | 
						|
    if (!isa<CXXThisExpr>(ignoreTemporariesAndPointers(ObjPtr.E))) {
 | 
						|
      Stream << ObjPtr.UsageIdentifier;
 | 
						|
      Stream << "->";
 | 
						|
    }
 | 
						|
 | 
						|
    Stream << MethodDecl->getName();
 | 
						|
  } else {
 | 
						|
    Stream << " { return ";
 | 
						|
    switch (LP.Callable.CM) {
 | 
						|
    case CM_ByValue:
 | 
						|
    case CM_ByRef:
 | 
						|
      if (LP.Callable.UsageIdentifier != LP.Callable.CaptureIdentifier) {
 | 
						|
        Stream << "(" << LP.Callable.UsageIdentifier << ")";
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      LLVM_FALLTHROUGH;
 | 
						|
    case CM_InitExpression:
 | 
						|
      Stream << LP.Callable.UsageIdentifier;
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      Ref->printPretty(Stream, nullptr, Result.Context->getPrintingPolicy());
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  Stream << "(";
 | 
						|
 | 
						|
  addFunctionCallArgs(getForwardedArgumentList(LP), Stream);
 | 
						|
  Stream << "); }";
 | 
						|
 | 
						|
  Diag << FixItHint::CreateReplacement(MatchedDecl->getSourceRange(),
 | 
						|
                                       Stream.str());
 | 
						|
}
 | 
						|
 | 
						|
} // namespace modernize
 | 
						|
} // namespace tidy
 | 
						|
} // namespace clang
 |