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