jbcoe created this revision.
jbcoe added reviewers: aaron.ballman, alexfh, djasper.
jbcoe added a subscriber: cfe-commits.
jbcoe set the repository for this revision to rL LLVM.

Replace std::bind with a lambda.

Not yet working for member functions.

Repository:
  rL LLVM

http://reviews.llvm.org/D16962

Files:
  clang-tidy/misc/AvoidStdBindCheck.cpp
  clang-tidy/misc/AvoidStdBindCheck.h
  clang-tidy/misc/CMakeLists.txt
  clang-tidy/misc/MiscTidyModule.cpp
  docs/clang-tidy/checks/list.rst
  docs/clang-tidy/checks/misc-avoid-std-bind.rst
  test/clang-tidy/misc-avoid-std-bind.cpp

Index: test/clang-tidy/misc-avoid-std-bind.cpp
===================================================================
--- /dev/null
+++ test/clang-tidy/misc-avoid-std-bind.cpp
@@ -0,0 +1,64 @@
+// RUN: %check_clang_tidy %s misc-avoid-std-bind %t
+
+namespace std {
+inline namespace impl {
+template <class Fp, class... Arguments>
+class bind_rt {};
+
+template <class Fp, class... Arguments>
+bind_rt<Fp, Arguments...> bind(Fp&&, Arguments&& ...);
+}
+}
+
+int add(int x, int y) { return x + y; }
+
+void f()
+{
+  auto clj = std::bind(add,2,2);
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: avoid using std::bind [misc-avoid-std-bind]
+}
+
+// CHECK-FIXES: auto clj = [] { return add(2, 2); };
+
+void g()
+{
+  int x = 2;
+  int y = 2;
+  auto clj = std::bind(add,x,y);
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: avoid using std::bind [misc-avoid-std-bind]
+}
+
+// CHECK-FIXES: auto clj = [=] { return add(x, y); };
+
+struct placeholder {};
+placeholder _1;
+placeholder _2;
+
+void h()
+{
+  int x = 2;
+  auto clj = std::bind(add,x,_1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: avoid using std::bind [misc-avoid-std-bind]
+}
+
+// CHECK-FIXES: auto clj = [=](int arg1) { return add(x, arg1); };
+
+struct A;
+struct B;
+bool ABTest(const A&, const B&);
+
+void i()
+{
+  auto BATest = std::bind(ABTest, _2, _1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: avoid using std::bind [misc-avoid-std-bind]
+}
+
+// CHECK-FIXES: auto BATest = [](const struct B & arg1, const struct A & arg2) { return ABTest(arg2, arg1); };
+
+void j()
+{
+  auto clj = std::bind(add, 2, 2, 2);
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: avoid using std::bind [misc-avoid-std-bind]
+}
+// No fix is applied for argument mismatches.
+// CHECK-FIXES: auto clj = std::bind(add, 2, 2, 2);
Index: docs/clang-tidy/checks/misc-avoid-std-bind.rst
===================================================================
--- /dev/null
+++ docs/clang-tidy/checks/misc-avoid-std-bind.rst
@@ -0,0 +1,13 @@
+.. title:: clang-tidy - misc-avoid-std-bind
+
+misc-avoid-std-bind
+===================
+
+Find uses of std::bind. Replace simple uses of std::bind with lambdas. Lambdas
+will use value-capture where required.
+
+Right now it only handles free functions not member functions.
+
+std::bind can result in larger object files and binaries due to type
+information that will not be produced by equivalent lambdas.
+
Index: docs/clang-tidy/checks/list.rst
===================================================================
--- docs/clang-tidy/checks/list.rst
+++ docs/clang-tidy/checks/list.rst
@@ -46,6 +46,7 @@
    misc-argument-comment
    misc-assert-side-effect
    misc-assign-operator-signature
+   misc-avoid-std-bind
    misc-bool-pointer-implicit-conversion
    misc-definitions-in-headers
    misc-inaccurate-erase
Index: clang-tidy/misc/MiscTidyModule.cpp
===================================================================
--- clang-tidy/misc/MiscTidyModule.cpp
+++ clang-tidy/misc/MiscTidyModule.cpp
@@ -13,6 +13,7 @@
 #include "ArgumentCommentCheck.h"
 #include "AssertSideEffectCheck.h"
 #include "AssignOperatorSignatureCheck.h"
+#include "AvoidStdBindCheck.h"
 #include "BoolPointerImplicitConversionCheck.h"
 #include "DefinitionsInHeadersCheck.h"
 #include "InaccurateEraseCheck.h"
@@ -48,6 +49,8 @@
         "misc-assert-side-effect");
     CheckFactories.registerCheck<AssignOperatorSignatureCheck>(
         "misc-assign-operator-signature");
+    CheckFactories.registerCheck<AvoidStdBindCheck>(
+        "misc-avoid-std-bind");
     CheckFactories.registerCheck<BoolPointerImplicitConversionCheck>(
         "misc-bool-pointer-implicit-conversion");
     CheckFactories.registerCheck<DefinitionsInHeadersCheck>(
Index: clang-tidy/misc/CMakeLists.txt
===================================================================
--- clang-tidy/misc/CMakeLists.txt
+++ clang-tidy/misc/CMakeLists.txt
@@ -4,6 +4,7 @@
   ArgumentCommentCheck.cpp
   AssertSideEffectCheck.cpp
   AssignOperatorSignatureCheck.cpp
+  AvoidStdBindCheck.cpp
   BoolPointerImplicitConversionCheck.cpp
   DefinitionsInHeadersCheck.cpp
   InaccurateEraseCheck.cpp
Index: clang-tidy/misc/AvoidStdBindCheck.h
===================================================================
--- /dev/null
+++ clang-tidy/misc/AvoidStdBindCheck.h
@@ -0,0 +1,59 @@
+//===--- AvoidStdBindCheck.h - clang-tidy-----------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_AVOID_STD_BIND_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_AVOID_STD_BIND_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Replace std::bind with a lambda.
+///
+/// FIXME: Add support for function references and member function references.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-avoid-std-bind.html
+class AvoidStdBindCheck : public ClangTidyCheck {
+public:
+  AvoidStdBindCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  struct BindArgument {
+    StringRef Tokens;
+    struct PlaceHolderInfo
+    {
+      std::string Name;
+      size_t BindPlaceholderIndex;
+      size_t BindFunctionArgIndex;
+    };
+    Optional<PlaceHolderInfo> PlaceHolder;
+    bool IsTemporaryExpr = false;
+  };
+
+  static std::vector<BindArgument>
+  buildBindArguments(const ast_matchers::MatchFinder::MatchResult &Result,
+                     const CallExpr *C);
+
+  static void addPlaceholderArgs(const std::vector<BindArgument> &Args,
+                                 const FunctionDecl *F,
+                                 llvm::raw_ostream &Stream);
+  static void addFunctionCallArgs(
+    const std::vector<BindArgument> &Args, llvm::raw_ostream &Stream);
+};
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_AVOID_STD_BIND_H
Index: clang-tidy/misc/AvoidStdBindCheck.cpp
===================================================================
--- /dev/null
+++ clang-tidy/misc/AvoidStdBindCheck.cpp
@@ -0,0 +1,148 @@
+//===--- AvoidStdBindCheck.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 <cassert>
+#include "AvoidStdBindCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+namespace {
+AST_MATCHER(NamedDecl, isStdBind) {
+  return Node.isInStdNamespace() && (Node.getName() == "bind");
+}
+} // end anonymous namespace
+
+static llvm::Regex MatchPlaceholder("^_([0-9]+)$");
+
+void AvoidStdBindCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(
+      callExpr(callee(namedDecl(isStdBind())),
+               hasArgument(0, declRefExpr(to(functionDecl().bind("f")))))
+          .bind("bind"),
+      this);
+}
+
+std::vector<AvoidStdBindCheck::BindArgument>
+AvoidStdBindCheck::buildBindArguments(const MatchFinder::MatchResult &Result,
+                                      const CallExpr *C) {
+    std::vector<BindArgument> BindArguments;
+  for (size_t I = 1, ArgCount = C->getNumArgs(); I < ArgCount; ++I) {
+    const Expr *E = C->getArg(I);
+    BindArgument B;
+    B.IsTemporaryExpr = dyn_cast<MaterializeTemporaryExpr>(E);
+    B.Tokens = Lexer::getSourceText(
+        CharSourceRange::getTokenRange(E->getLocStart(), E->getLocEnd()),
+        *Result.SourceManager, Result.Context->getLangOpts());
+
+    SmallVector<StringRef, 2> Matches;
+    if (MatchPlaceholder.match(B.Tokens, &Matches))
+    {  B.PlaceHolder = BindArgument::PlaceHolderInfo{};
+       B.PlaceHolder->Name = (llvm::Twine("arg") + Matches[1]).str();
+       B.PlaceHolder->BindPlaceholderIndex = std::stoi(Matches[1]);
+       B.PlaceHolder->BindFunctionArgIndex = I-1;
+    }
+    BindArguments.push_back(B);
+  }
+  return BindArguments;
+}
+
+void AvoidStdBindCheck::addPlaceholderArgs(
+    const std::vector<BindArgument> &Args, const FunctionDecl *F,
+    llvm::raw_ostream &Stream) {
+  size_t PlaceholderCount = std::count_if(
+      Args.begin(), Args.end(), [](const auto &B) { return B.PlaceHolder; });
+
+  if (PlaceholderCount)
+    Stream << "(";
+
+  StringRef Delimiter = "";
+  for (size_t I = 0; I < PlaceholderCount; ++I) {
+
+    // Bind placeholders start with index 1 so we have to do offsetting.
+    auto FindIt =
+        std::find_if(Args.begin(), Args.end(), [I](const BindArgument &B) {
+          return B.PlaceHolder && B.PlaceHolder->BindPlaceholderIndex == I + 1;
+        });
+    assert(FindIt != Args.end());
+
+    const BindArgument::PlaceHolderInfo &P = *FindIt->PlaceHolder;
+
+    const ParmVarDecl *PDecl = F->getParamDecl(P.BindFunctionArgIndex);
+    StringRef ParmTypeName = PDecl->getType().getAsString();
+    Stream << Delimiter << ParmTypeName << " arg" << I + 1;
+    Delimiter = ", ";
+  }
+
+  if (PlaceholderCount)
+    Stream << ")";
+}
+
+void AvoidStdBindCheck::addFunctionCallArgs(
+    const std::vector<BindArgument> &Args, llvm::raw_ostream &Stream) {
+  StringRef Delimiter = "";
+  for (const auto &B : Args) {
+    if (B.PlaceHolder)
+      Stream << Delimiter << B.PlaceHolder->Name;
+    else
+      Stream << Delimiter << B.Tokens;
+    Delimiter = ", ";
+  }
+  Stream << "); };";
+}
+
+void AvoidStdBindCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *MatchedDecl = Result.Nodes.getNodeAs<CallExpr>("bind");
+  auto DiagnosticBuilder =
+      diag(MatchedDecl->getLocStart(), "avoid using std::bind");
+
+  if (!getLangOpts().CPlusPlus11) // Need C++11 for lambdas
+    return;
+
+  std::string Buffer;
+  llvm::raw_string_ostream Stream(Buffer);
+  const std::vector<BindArgument> Args =
+      buildBindArguments(Result, MatchedDecl);
+
+  bool HasCapturedArgument =
+      std::find_if(Args.begin(), Args.end(), [](const BindArgument &B) {
+        return !B.IsTemporaryExpr && !B.PlaceHolder;
+      }) != Args.end();
+
+  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.
+  if (F->getNumParams() != Args.size())
+    return;
+
+  Stream << "[" << (HasCapturedArgument ? "=" : "") << "]";
+  addPlaceholderArgs(Args, F, Stream);
+  Stream << " { return " << F->getName() << "(";
+  addFunctionCallArgs(Args, Stream);
+
+  SourceRange ReplacedRange(
+      MatchedDecl->getLocStart(),
+      Lexer::getLocForEndOfToken(MatchedDecl->getLocEnd(), 0,
+                                 *Result.SourceManager,
+                                 Result.Context->getLangOpts()));
+
+  DiagnosticBuilder << FixItHint::CreateReplacement(ReplacedRange,
+                                                    Stream.str());
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to