zturner created this revision.
zturner added reviewers: aaron.ballman, jbcoe.

Previously modernize-avoid-bind only supported the case where the function to 
call was a FunctionDecl.  This patch makes it support arbitrary expressions, 
including functors, member functions, and combinations thereof.

This change actually *simplifies* the code rather than complicates it, because 
it assumes that the first argument to std::bind() is always a callable, 
otherwise it wouldn't even compile.  So rather than limiting ourselves to 
DeclRefExprs, we just accept any kind of expression for the first argument.  
Fixits are still only applied in the same set of limited cases as before, 
although I plan to improve this in followup patches.


https://reviews.llvm.org/D69872

Files:
  clang-tools-extra/clang-tidy/modernize/AvoidBindCheck.cpp
  clang-tools-extra/test/clang-tidy/checkers/modernize-avoid-bind.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/modernize-avoid-bind.cpp
===================================================================
--- clang-tools-extra/test/clang-tidy/checkers/modernize-avoid-bind.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/modernize-avoid-bind.cpp
@@ -78,6 +78,39 @@
   // CHECK-FIXES: auto clj = [] { return C::add(1, 1); };
 }
 
+struct D {
+  void operator()(int x, int y) {}
+
+  void MemberFunction(int x) {}
+};
+
+void o() {
+  D d;
+  auto clj = std::bind(d, 1, 2);
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind
+  // CHECK-FIXES: auto clj = std::bind(d, 1, 2);
+}
+
+void p() {
+  auto clj = std::bind(D{}, 1, 2);
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind
+  // CHECK-FIXES: auto clj = std::bind(D{}, 1, 2);
+}
+
+void q() {
+  D *d;
+  auto clj = std::bind(*d, 1, 2);
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind
+  // CHECK-FIXES: auto clj = std::bind(*d, 1, 2);
+}
+
+void r() {
+  D *d;
+  auto clj = std::bind(&D::MemberFunction, d, 1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind
+  // CHECK-FIXES: auto clj = std::bind(&D::MemberFunction, d, 1);
+}
+
 // Let's fake a minimal std::function-like facility.
 namespace std {
 template <typename _Tp>
Index: clang-tools-extra/clang-tidy/modernize/AvoidBindCheck.cpp
===================================================================
--- clang-tools-extra/clang-tidy/modernize/AvoidBindCheck.cpp
+++ clang-tools-extra/clang-tidy/modernize/AvoidBindCheck.cpp
@@ -120,12 +120,10 @@
   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("ref")))
-          .bind("bind"),
-      this);
+  Finder->addMatcher(callExpr(callee(namedDecl(hasName("::std::bind"))),
+                              hasArgument(0, expr().bind("ref")))
+                         .bind("bind"),
+                     this);
 }
 
 void AvoidBindCheck::check(const MatchFinder::MatchResult &Result) {
@@ -148,21 +146,27 @@
   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())
+  const auto *Ref = Result.Nodes.getNodeAs<DeclRefExpr>("ref");
+  if (Ref) {
+    const auto *FD = llvm::dyn_cast_or_null<FunctionDecl>(Ref->getFoundDecl());
+
+    // 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 (!FD || (FD->getNumParams() != Args.size()))
+      return;
+  } else {
+    // Don't support fixits for generalized call expressions.
+    // FIXME: Support fixits for member function pointers as well as call objects.
     return;
+  }
 
   std::string Buffer;
   llvm::raw_string_ostream Stream(Buffer);
 
   bool HasCapturedArgument = llvm::any_of(
       Args, [](const BindArgument &B) { return B.Kind == BK_Other; });
-  const auto *Ref = Result.Nodes.getNodeAs<DeclRefExpr>("ref");
   Stream << "[" << (HasCapturedArgument ? "=" : "") << "]";
   addPlaceholderArgs(Args, Stream);
   Stream << " { return ";
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to