[clang-tools-extra] r269341 - [clang-tidy] Adds modernize-avoid-bind check
Jonathan Coe via cfe-commits
cfe-commits at lists.llvm.org
Thu May 12 13:06:04 PDT 2016
Author: jbcoe
Date: Thu May 12 15:06:04 2016
New Revision: 269341
URL: http://llvm.org/viewvc/llvm-project?rev=269341&view=rev
Log:
[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
Added:
clang-tools-extra/trunk/clang-tidy/modernize/AvoidBindCheck.cpp
clang-tools-extra/trunk/clang-tidy/modernize/AvoidBindCheck.h
clang-tools-extra/trunk/docs/clang-tidy/checks/modernize-avoid-bind.rst
clang-tools-extra/trunk/test/clang-tidy/modernize-avoid-bind.cpp
Modified:
clang-tools-extra/trunk/clang-tidy/modernize/CMakeLists.txt
clang-tools-extra/trunk/clang-tidy/modernize/ModernizeTidyModule.cpp
clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst
Added: clang-tools-extra/trunk/clang-tidy/modernize/AvoidBindCheck.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/modernize/AvoidBindCheck.cpp?rev=269341&view=auto
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/modernize/AvoidBindCheck.cpp (added)
+++ clang-tools-extra/trunk/clang-tidy/modernize/AvoidBindCheck.cpp Thu May 12 15:06:04 2016
@@ -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
Added: clang-tools-extra/trunk/clang-tidy/modernize/AvoidBindCheck.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/modernize/AvoidBindCheck.h?rev=269341&view=auto
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/modernize/AvoidBindCheck.h (added)
+++ clang-tools-extra/trunk/clang-tidy/modernize/AvoidBindCheck.h Thu May 12 15:06:04 2016
@@ -0,0 +1,36 @@
+//===--- AvoidBindCheck.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_MODERNIZE_AVOID_BIND_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_AVOID_BIND_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// Replace simple uses of 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/modernize-avoid-std-bind.html
+class AvoidBindCheck : public ClangTidyCheck {
+public:
+ AvoidBindCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_AVOID_BIND_H
Modified: clang-tools-extra/trunk/clang-tidy/modernize/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/modernize/CMakeLists.txt?rev=269341&r1=269340&r2=269341&view=diff
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/modernize/CMakeLists.txt (original)
+++ clang-tools-extra/trunk/clang-tidy/modernize/CMakeLists.txt Thu May 12 15:06:04 2016
@@ -1,6 +1,7 @@
set(LLVM_LINK_COMPONENTS support)
add_clang_library(clangTidyModernizeModule
+ AvoidBindCheck.cpp
DeprecatedHeadersCheck.cpp
LoopConvertCheck.cpp
LoopConvertUtils.cpp
Modified: clang-tools-extra/trunk/clang-tidy/modernize/ModernizeTidyModule.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/modernize/ModernizeTidyModule.cpp?rev=269341&r1=269340&r2=269341&view=diff
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/modernize/ModernizeTidyModule.cpp (original)
+++ clang-tools-extra/trunk/clang-tidy/modernize/ModernizeTidyModule.cpp Thu May 12 15:06:04 2016
@@ -10,6 +10,7 @@
#include "../ClangTidy.h"
#include "../ClangTidyModule.h"
#include "../ClangTidyModuleRegistry.h"
+#include "AvoidBindCheck.h"
#include "DeprecatedHeadersCheck.h"
#include "LoopConvertCheck.h"
#include "MakeSharedCheck.h"
@@ -34,6 +35,8 @@ namespace modernize {
class ModernizeModule : public ClangTidyModule {
public:
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+ CheckFactories.registerCheck<AvoidBindCheck>(
+ "modernize-avoid-bind");
CheckFactories.registerCheck<DeprecatedHeadersCheck>(
"modernize-deprecated-headers");
CheckFactories.registerCheck<LoopConvertCheck>("modernize-loop-convert");
Modified: clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst?rev=269341&r1=269340&r2=269341&view=diff
==============================================================================
--- clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst (original)
+++ clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst Thu May 12 15:06:04 2016
@@ -91,6 +91,7 @@ Clang-Tidy Checks
misc-unused-raii
misc-unused-using-decls
misc-virtual-near-miss
+ modernize-avoid-bind
modernize-deprecated-headers
modernize-loop-convert
modernize-make-shared
Added: clang-tools-extra/trunk/docs/clang-tidy/checks/modernize-avoid-bind.rst
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/docs/clang-tidy/checks/modernize-avoid-bind.rst?rev=269341&view=auto
==============================================================================
--- clang-tools-extra/trunk/docs/clang-tidy/checks/modernize-avoid-bind.rst (added)
+++ clang-tools-extra/trunk/docs/clang-tidy/checks/modernize-avoid-bind.rst Thu May 12 15:06:04 2016
@@ -0,0 +1,38 @@
+.. title:: clang-tidy - modernize-avoid-std-bind
+
+modernize-avoid-bind
+==========================
+
+The check finds uses of ``std::bind`` and replaces simple uses with lambdas.
+Lambdas will use value-capture where required.
+
+Right now it only handles free functions, not member functions.
+
+Given:
+
+.. code:: C++
+
+ int add(int x, int y) { return x + y; }
+
+Then:
+
+.. code:: C++
+
+ void f() {
+ int x = 2;
+ auto clj = std::bind(add, x, _1);
+ }
+
+is replaced by:
+
+.. code:: C++
+
+ void f() {
+ int x = 2;
+ auto clj = [=](auto && arg1) { return add(x, arg1); };
+ }
+
+``std::bind`` can be hard to read and can result in larger object files and
+binaries due to type information that will not be produced by equivalent
+lambdas.
+
Added: clang-tools-extra/trunk/test/clang-tidy/modernize-avoid-bind.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clang-tidy/modernize-avoid-bind.cpp?rev=269341&view=auto
==============================================================================
--- clang-tools-extra/trunk/test/clang-tidy/modernize-avoid-bind.cpp (added)
+++ clang-tools-extra/trunk/test/clang-tidy/modernize-avoid-bind.cpp Thu May 12 15:06:04 2016
@@ -0,0 +1,70 @@
+// RUN: %check_clang_tidy %s modernize-avoid-bind %t -- -- -std=c++14
+
+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: prefer a lambda to std::bind [modernize-avoid-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: prefer a lambda to 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: prefer a lambda to std::bind
+ // CHECK-FIXES: auto clj = [=](auto && 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: prefer a lambda to std::bind
+ // CHECK-FIXES: auto BATest = [](auto && arg1, auto && arg2) { return ABTest(arg2, arg1); };
+}
+
+void j() {
+ auto clj = std::bind(add, 2, 2, 2);
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind
+ // No fix is applied for argument mismatches.
+ // CHECK-FIXES: auto clj = std::bind(add, 2, 2, 2);
+}
+
+void k() {
+ auto clj = std::bind(add, _1, _1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind
+ // No fix is applied for reused placeholders.
+ // CHECK-FIXES: auto clj = std::bind(add, _1, _1);
+}
+
+void m() {
+ auto clj = std::bind(add, 1, add(2, 5));
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind
+ // No fix is applied for nested calls.
+ // CHECK-FIXES: auto clj = std::bind(add, 1, add(2, 5));
+}
+
More information about the cfe-commits
mailing list