10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/Lex/Lexer.h"
14 #include <unordered_map>
16 using namespace clang::ast_matchers;
23 enum BindArgumentKind { BK_Temporary, BK_Placeholder, BK_CallExpr, BK_Other };
27 BindArgumentKind
Kind = BK_Other;
33 static SmallVector<BindArgument, 4>
35 SmallVector<BindArgument, 4> BindArguments;
36 llvm::Regex MatchPlaceholder(
"^_([0-9]+)$");
39 for (
size_t I = 1, ArgCount = C->getNumArgs(); I < ArgCount; ++I) {
40 const Expr *E = C->getArg(I);
42 if (
const auto *M = dyn_cast<MaterializeTemporaryExpr>(E)) {
43 const auto *TE = M->GetTemporaryExpr();
44 B.Kind = isa<CallExpr>(TE) ? BK_CallExpr : BK_Temporary;
47 B.Tokens = Lexer::getSourceText(
48 CharSourceRange::getTokenRange(E->getLocStart(), E->getLocEnd()),
49 *Result.SourceManager, Result.Context->getLangOpts());
51 SmallVector<StringRef, 2> Matches;
52 if (B.Kind == BK_Other && MatchPlaceholder.match(B.Tokens, &Matches)) {
53 B.Kind = BK_Placeholder;
54 B.PlaceHolderIndex = std::stoi(Matches[1]);
56 BindArguments.push_back(B);
62 llvm::raw_ostream &Stream) {
63 auto MaxPlaceholderIt =
64 std::max_element(Args.begin(), Args.end(),
65 [](
const BindArgument &B1,
const BindArgument &B2) {
66 return B1.PlaceHolderIndex < B2.PlaceHolderIndex;
70 if (MaxPlaceholderIt == Args.end() || MaxPlaceholderIt->PlaceHolderIndex == 0)
73 size_t PlaceholderCount = MaxPlaceholderIt->PlaceHolderIndex;
75 StringRef Delimiter =
"";
76 for (
size_t I = 1; I <= PlaceholderCount; ++I) {
77 Stream << Delimiter <<
"auto && arg" << I;
84 llvm::raw_ostream &Stream) {
85 StringRef Delimiter =
"";
86 for (
const auto &B : Args) {
87 if (B.PlaceHolderIndex)
88 Stream << Delimiter <<
"arg" << B.PlaceHolderIndex;
90 Stream << Delimiter << B.Tokens;
96 llvm::SmallSet<size_t, 4> PlaceHolderIndices;
97 for (
const BindArgument &B : Args) {
98 if (B.PlaceHolderIndex) {
99 if (!PlaceHolderIndices.insert(B.PlaceHolderIndex).second)
106 void AvoidBindCheck::registerMatchers(MatchFinder *
Finder) {
107 if (!getLangOpts().CPlusPlus14)
111 callExpr(callee(namedDecl(hasName(
"::std::bind"))),
112 hasArgument(0, declRefExpr(to(functionDecl().bind(
"f")))))
117 void AvoidBindCheck::check(
const MatchFinder::MatchResult &
Result) {
118 const auto *MatchedDecl = Result.Nodes.getNodeAs<CallExpr>(
"bind");
119 auto Diag = diag(MatchedDecl->getLocStart(),
"prefer a lambda to std::bind");
127 if (llvm::any_of(Args,
128 [](
const BindArgument &B) {
return B.Kind == BK_CallExpr; }))
137 const auto *F = Result.Nodes.getNodeAs<FunctionDecl>(
"f");
143 if (F->getNumParams() != Args.size())
147 llvm::raw_string_ostream Stream(Buffer);
149 bool HasCapturedArgument = llvm::any_of(
150 Args, [](
const BindArgument &B) {
return B.Kind == BK_Other; });
152 Stream <<
"[" << (HasCapturedArgument ?
"=" :
"") <<
"]";
154 Stream <<
" { return " << F->getName() <<
"(";
158 Diag << FixItHint::CreateReplacement(MatchedDecl->getSourceRange(), Stream.str());
std::unique_ptr< ast_matchers::MatchFinder > Finder
static bool isPlaceHolderIndexRepeated(const ArrayRef< BindArgument > Args)
static void addFunctionCallArgs(const ArrayRef< BindArgument > Args, llvm::raw_ostream &Stream)
static void addPlaceholderArgs(const ArrayRef< BindArgument > Args, llvm::raw_ostream &Stream)
static SmallVector< BindArgument, 4 > buildBindArguments(const MatchFinder::MatchResult &Result, const CallExpr *C)