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
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits