boga95 updated this revision to Diff 166994.
boga95 marked 12 inline comments as done.

https://reviews.llvm.org/D52281

Files:
  clang-tidy/modernize/CMakeLists.txt
  clang-tidy/modernize/ModernizeTidyModule.cpp
  clang-tidy/modernize/ReplaceGenericFunctorCallCheck.cpp
  clang-tidy/modernize/ReplaceGenericFunctorCallCheck.h
  docs/ReleaseNotes.rst
  docs/clang-tidy/checks/list.rst
  docs/clang-tidy/checks/modernize-replace-generic-functor-call.rst
  test/clang-tidy/modernize-replace-generic-functor-call.cpp

Index: test/clang-tidy/modernize-replace-generic-functor-call.cpp
===================================================================
--- /dev/null
+++ test/clang-tidy/modernize-replace-generic-functor-call.cpp
@@ -0,0 +1,56 @@
+// RUN: %check_clang_tidy %s modernize-replace-generic-functor-call %t -- -- -std=c++17
+
+namespace std {
+
+template <class T>
+T max(T a, T b) {
+  return a < b ? b : a;
+}
+
+struct function {
+  void operator()();
+};
+
+} // namespace std
+
+struct Foo {
+  static void print_() {}
+  void print() const {}
+  int num_;
+};
+
+// Something that triggers the check
+template <class T>
+void func2(T func) {
+  func(1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Use ::std::invoke to invoke type dependent callable objects. [modernize-replace-generic-functor-call]
+  // CHECK-FIXES: ::std::invoke(func, 1);
+  func();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Use ::std::invoke to invoke type dependent callable objects. [modernize-replace-generic-functor-call]
+  // CHECK-FIXES: ::std::invoke(func);
+
+  Foo foo;
+  (foo.*func)(1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Use ::std::invoke to invoke type dependent callable objects. [modernize-replace-generic-functor-call]
+  // CHECK-FIXES: ::std::invoke(foo, func, 1);
+  (foo.*func)();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Use ::std::invoke to invoke type dependent callable objects. [modernize-replace-generic-functor-call]
+  // CHECK-FIXES: ::std::invoke(foo, func);
+  (Foo().*func)(1, 2);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Use ::std::invoke to invoke type dependent callable objects. [modernize-replace-generic-functor-call]
+  // CHECK-FIXES: ::std::invoke(Foo(), func, 1, 2);
+}
+
+// Something that doesn't triggers the check
+void func3() {}
+
+void g(std::function func, int (*fp)(int), void (Foo::*mfp)(int)) {
+  func3();        // Regular function call
+  std::max(1, 2); // Template function call
+  Foo::print_();  // Static function call
+  []() {}();      // Lambda call
+  func();         // Call through std::function
+  fp(3);          // Call through function pointer
+  Foo foo;
+  (foo.*(mfp))(1); // Call through member function pointer
+}
Index: docs/clang-tidy/checks/modernize-replace-generic-functor-call.rst
===================================================================
--- /dev/null
+++ docs/clang-tidy/checks/modernize-replace-generic-functor-call.rst
@@ -0,0 +1,39 @@
+.. title:: clang-tidy - modernize-replace-generic-functor-call
+
+modernize-replace-generic-functor-call
+======================================
+
+Replaces type dependent functor, function pointer and pointer to member call
+to the proper ``std::invoke()`` in C++17 or newer codebases.
+  
+Examples:
+
+.. code-block:: c++
+
+  template<class T1, class T2>
+  void f(T1 functionPointer, T2 memberFunctionPointer) {
+    functionPointer(2); // Replace to ::std::invoke(functionPointer, 2)
+
+    Foo foo;
+    (foo.*memberFunctionPointer)(1, 2); // Replace to ::std::invoke(foo, memberFunctionPointer, 1, 2)
+
+    (Foo().*memberFunctionPointer)(3); // Replace to ::std::invoke(Foo(), memberFunctionPointer, 3)
+  }
+
+  // Neither of them is type dependent, no diagnose
+  void g(std::function func, int (*functionPointer)(int), void (Foo::*memberFunctionPointer)(int)) {
+    freeFunction();
+
+    std::max(1,2);
+
+    Foo::print_();
+
+    [](){}();
+
+    func();
+
+    functionPointer(3);
+
+    Foo foo;
+    (foo.*(memberFunctionPointer))(1);
+  }
Index: docs/clang-tidy/checks/list.rst
===================================================================
--- docs/clang-tidy/checks/list.rst
+++ docs/clang-tidy/checks/list.rst
@@ -171,6 +171,7 @@
    modernize-raw-string-literal
    modernize-redundant-void-arg
    modernize-replace-auto-ptr
+   modernize-replace-generic-functor-call
    modernize-replace-random-shuffle
    modernize-return-braced-init-list
    modernize-shrink-to-fit
Index: docs/ReleaseNotes.rst
===================================================================
--- docs/ReleaseNotes.rst
+++ docs/ReleaseNotes.rst
@@ -142,6 +142,12 @@
 
   Checks on ``switch`` and ``if`` - ``else if`` constructs that do not cover all possible code paths.
 
+- New :doc:`modernize-replace-generic-functor-call
+  <clang-tidy/checks/modernize-replace-generic-functor-call>` check.
+
+  Replaces type dependent functor, function pointer and pointer to member call
+  to the proper ``std::invoke()`` in C++17 or newer codebases.
+  
 - New :doc:`modernize-use-uncaught-exceptions
   <clang-tidy/checks/modernize-use-uncaught-exceptions>` check.
 
Index: clang-tidy/modernize/ReplaceGenericFunctorCallCheck.h
===================================================================
--- /dev/null
+++ clang-tidy/modernize/ReplaceGenericFunctorCallCheck.h
@@ -0,0 +1,43 @@
+//===--- ReplaceGenericFunctorCallCheck.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_REPLACEGENERICFUNCTORCALLCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_REPLACEGENERICFUNCTORCALLCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// Use std::invoke to call callable objects in generic code.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-replace-generic-functor-call.html
+class ReplaceGenericFunctorCallCheck : public ClangTidyCheck {
+public:
+  ReplaceGenericFunctorCallCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  void diagnose(const CallExpr *Functor, const std::string &Param,
+                const std::string &OriginalParam);
+  std::string strFromLoc(const SourceManager *Source,
+                         const SourceLocation &Start,
+                         const SourceLocation &End);
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_REPLACEGENERICFUNCTORCALLCHECK_H
Index: clang-tidy/modernize/ReplaceGenericFunctorCallCheck.cpp
===================================================================
--- /dev/null
+++ clang-tidy/modernize/ReplaceGenericFunctorCallCheck.cpp
@@ -0,0 +1,114 @@
+//===--- ReplaceGenericFunctorCallCheck.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 "ReplaceGenericFunctorCallCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/ADT/Twine.h"
+#include <string>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+void ReplaceGenericFunctorCallCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus17)
+    return;
+
+  // template<class T>
+  // void f(T func) {
+  //   func();
+  //   ^~~~~~~
+  //   ::std::invoke(func, 1)
+  Finder->addMatcher(callExpr(has(declRefExpr())).bind("functor"), this);
+
+  // template<class T>
+  // void f(T mfp) {
+  //   Foo foo;
+  //   (foo.*mfp)();
+  //   ^~~~~~~~~~~~~
+  //   ::std::invoke(foo, func, 1)
+  Finder->addMatcher(
+      callExpr(has(parenExpr(has(binaryOperator(hasOperatorName(".*"))))))
+          .bind("mfunctor"),
+      this);
+}
+
+void ReplaceGenericFunctorCallCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const SourceManager *Source = Result.SourceManager;
+
+  const auto *Functor = Result.Nodes.getNodeAs<CallExpr>("functor");
+  if (Functor && Functor->isTypeDependent()) {
+    const SourceLocation EndLoc = Functor->getNumArgs() == 0
+                                      ? Functor->getRParenLoc()
+                                      : Functor->getArg(0)->getLocStart();
+    const std::string Param =
+        strFromLoc(Source, Functor->getLocStart(), EndLoc.getLocWithOffset(-1));
+    const std::string OriginalParams =
+        Functor->getNumArgs() == 0
+            ? ""
+            : strFromLoc(Source, Functor->getArg(0)->getLocStart(),
+                         Functor->getLocEnd());
+
+    diagnose(Functor, Param, OriginalParams + ")");
+    return;
+  }
+
+  const auto *MFunctor = Result.Nodes.getNodeAs<CallExpr>("mfunctor");
+  if (MFunctor && MFunctor->isTypeDependent()) {
+    const auto *Paren = dyn_cast<ParenExpr>(MFunctor->getCallee());
+    const auto *BinOp = dyn_cast<BinaryOperator>(Paren->getSubExpr());
+
+    const Expr *Obj = BinOp->getLHS();
+    const std::string ObjName =
+        strFromLoc(Source, Obj->getLocStart(), BinOp->getOperatorLoc());
+
+    const Expr *MFunc = BinOp->getRHS();
+    const char *MFuncEnd = Source->getCharacterData(Paren->getRParen());
+    const std::string FuncName =
+        strFromLoc(Source, MFunc->getLocStart(), Paren->getRParen());
+
+    std::string OriginalParams(MFuncEnd + 2,
+                               Source->getCharacterData(MFunctor->getLocEnd()) -
+                                   (MFuncEnd + 1));
+
+    const std::string Param = ObjName + ", " + FuncName;
+    diagnose(MFunctor, Param, OriginalParams);
+    return;
+  }
+}
+
+void ReplaceGenericFunctorCallCheck::diagnose(
+    const CallExpr *Functor, const std::string &Param,
+    const std::string &OriginalParams) {
+  const Twine &Replace = Twine("::std::invoke(") + Param +
+                         (Functor->getNumArgs() == 0 ? "" : ", ") +
+                         OriginalParams;
+  diag(Functor->getExprLoc(),
+       "Use ::std::invoke to invoke type dependent callable objects.")
+      << FixItHint::CreateReplacement(Functor->getSourceRange(), Replace.str());
+}
+
+std::string
+ReplaceGenericFunctorCallCheck::strFromLoc(const SourceManager *Source,
+                                           const SourceLocation &Start,
+                                           const SourceLocation &End) {
+  const char *StartPos = Source->getCharacterData(Start);
+  const char *EndPos = Source->getCharacterData(End);
+  int Len = EndPos - StartPos >= 0 ? EndPos - StartPos : 0;
+  return std::string(StartPos, Len);
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
Index: clang-tidy/modernize/ModernizeTidyModule.cpp
===================================================================
--- clang-tidy/modernize/ModernizeTidyModule.cpp
+++ clang-tidy/modernize/ModernizeTidyModule.cpp
@@ -19,6 +19,7 @@
 #include "RawStringLiteralCheck.h"
 #include "RedundantVoidArgCheck.h"
 #include "ReplaceAutoPtrCheck.h"
+#include "ReplaceGenericFunctorCallCheck.h"
 #include "ReplaceRandomShuffleCheck.h"
 #include "ReturnBracedInitListCheck.h"
 #include "ShrinkToFitCheck.h"
@@ -58,6 +59,8 @@
         "modernize-redundant-void-arg");
     CheckFactories.registerCheck<ReplaceAutoPtrCheck>(
         "modernize-replace-auto-ptr");
+    CheckFactories.registerCheck<ReplaceGenericFunctorCallCheck>(
+        "modernize-replace-generic-functor-call");
     CheckFactories.registerCheck<ReplaceRandomShuffleCheck>(
         "modernize-replace-random-shuffle");
     CheckFactories.registerCheck<ReturnBracedInitListCheck>(
Index: clang-tidy/modernize/CMakeLists.txt
===================================================================
--- clang-tidy/modernize/CMakeLists.txt
+++ clang-tidy/modernize/CMakeLists.txt
@@ -13,6 +13,7 @@
   RawStringLiteralCheck.cpp
   RedundantVoidArgCheck.cpp
   ReplaceAutoPtrCheck.cpp
+  ReplaceGenericFunctorCallCheck.cpp
   ReplaceRandomShuffleCheck.cpp
   ReturnBracedInitListCheck.cpp
   ShrinkToFitCheck.cpp
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to