diff options
| author | Jonathan Coe <jbcoe@me.com> | 2016-05-12 20:06:04 +0000 |
|---|---|---|
| committer | Jonathan Coe <jbcoe@me.com> | 2016-05-12 20:06:04 +0000 |
| commit | a30c69c9e43bef7a1e037a3f183531543f7676b0 (patch) | |
| tree | 7571d9d1281e88bbdfc656427caa24cf95fe0d28 /clang-tools-extra/clang-tidy/modernize/AvoidBindCheck.cpp | |
| parent | 3fe71581743ceb838b113ee728e6de7f7ee8d25d (diff) | |
| download | bcm5719-llvm-a30c69c9e43bef7a1e037a3f183531543f7676b0.tar.gz bcm5719-llvm-a30c69c9e43bef7a1e037a3f183531543f7676b0.zip | |
[clang-tidy] Adds modernize-avoid-bind check
Summary:
This patch adds a check that replaces std::bind with a lambda.
Not yet working for member functions.
Reviewers: aaron.ballman, alexfh
Subscribers: cfe-commits
Differential Revision: http://reviews.llvm.org/D16962
llvm-svn: 269341
Diffstat (limited to 'clang-tools-extra/clang-tidy/modernize/AvoidBindCheck.cpp')
| -rw-r--r-- | clang-tools-extra/clang-tidy/modernize/AvoidBindCheck.cpp | 163 |
1 files changed, 163 insertions, 0 deletions
diff --git a/clang-tools-extra/clang-tidy/modernize/AvoidBindCheck.cpp b/clang-tools-extra/clang-tidy/modernize/AvoidBindCheck.cpp new file mode 100644 index 00000000000..c7c51a25e99 --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/AvoidBindCheck.cpp @@ -0,0 +1,163 @@ +//===--- AvoidBindCheck.cpp - clang-tidy--------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "AvoidBindCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Lex/Lexer.h" +#include <cassert> +#include <unordered_map> + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace modernize { + +namespace { +enum BindArgumentKind { BK_Temporary, BK_Placeholder, BK_CallExpr, BK_Other }; + +struct BindArgument { + StringRef Tokens; + BindArgumentKind Kind = BK_Other; + size_t PlaceHolderIndex = 0; +}; + +} // end namespace + +static SmallVector<BindArgument, 4> +buildBindArguments(const MatchFinder::MatchResult &Result, const CallExpr *C) { + SmallVector<BindArgument, 4> BindArguments; + llvm::Regex MatchPlaceholder("^_([0-9]+)$"); + + // Start at index 1 as first argument to bind is the function name. + for (size_t I = 1, ArgCount = C->getNumArgs(); I < ArgCount; ++I) { + const Expr *E = C->getArg(I); + BindArgument B; + if (const auto *M = dyn_cast<MaterializeTemporaryExpr>(E)) { + const auto *TE = M->GetTemporaryExpr(); + B.Kind = isa<CallExpr>(TE) ? BK_CallExpr : BK_Temporary; + } + + B.Tokens = Lexer::getSourceText( + CharSourceRange::getTokenRange(E->getLocStart(), E->getLocEnd()), + *Result.SourceManager, Result.Context->getLangOpts()); + + SmallVector<StringRef, 2> Matches; + if (B.Kind == BK_Other && MatchPlaceholder.match(B.Tokens, &Matches)) { + B.Kind = BK_Placeholder; + B.PlaceHolderIndex = std::stoi(Matches[1]); + } + BindArguments.push_back(B); + } + return BindArguments; +} + +static void addPlaceholderArgs(const ArrayRef<BindArgument> Args, + llvm::raw_ostream &Stream) { + 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 (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 && arg" << I; + Delimiter = ", "; + } + Stream << ")"; +} + +static void addFunctionCallArgs(const ArrayRef<BindArgument> Args, + llvm::raw_ostream &Stream) { + StringRef Delimiter = ""; + for (const auto &B : Args) { + if (B.PlaceHolderIndex) + Stream << Delimiter << "arg" << B.PlaceHolderIndex; + else + Stream << Delimiter << B.Tokens; + 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; +} + +void AvoidBindCheck::registerMatchers(MatchFinder *Finder) { + if (!getLangOpts().CPlusPlus14) // Need C++14 for generic lambdas. + return; + + Finder->addMatcher( + callExpr(callee(namedDecl(hasName("::std::bind"))), + hasArgument(0, declRefExpr(to(functionDecl().bind("f"))))) + .bind("bind"), + this); +} + +void AvoidBindCheck::check(const MatchFinder::MatchResult &Result) { + const auto *MatchedDecl = Result.Nodes.getNodeAs<CallExpr>("bind"); + auto Diag = diag(MatchedDecl->getLocStart(), "prefer a lambda to std::bind"); + + const auto Args = buildBindArguments(Result, MatchedDecl); + + // Do not attempt to create fixits for nested call expressions. + // FIXME: Create lambda capture variables to capture output of calls. + // NOTE: Supporting nested std::bind will be more difficult due to placeholder + // sharing between outer and inner std:bind invocations. + if (llvm::any_of(Args, + [](const BindArgument &B) { return B.Kind == BK_CallExpr; })) + return; + + // 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; + + const auto *F = Result.Nodes.getNodeAs<FunctionDecl>("f"); + + // std::bind can support argument count mismatch between its arguments and the + // bound function's arguments. Do not attempt to generate a fixit for such + // cases. + // FIXME: Support this case by creating unused lambda capture variables. + if (F->getNumParams() != Args.size()) + return; + + std::string Buffer; + llvm::raw_string_ostream Stream(Buffer); + + bool HasCapturedArgument = llvm::any_of( + Args, [](const BindArgument &B) { return B.Kind == BK_Other; }); + + Stream << "[" << (HasCapturedArgument ? "=" : "") << "]"; + addPlaceholderArgs(Args, Stream); + Stream << " { return " << F->getName() << "("; + addFunctionCallArgs(Args, Stream); + Stream << "); };"; + + Diag << FixItHint::CreateReplacement(MatchedDecl->getSourceRange(), Stream.str()); +} + +} // namespace modernize +} // namespace tidy +} // namespace clang |

