PiotrZSL updated this revision to Diff 503297.
PiotrZSL added a comment.

Rebase due to broken baseline


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D144347/new/

https://reviews.llvm.org/D144347

Files:
  clang-tools-extra/clang-tidy/readability/CMakeLists.txt
  clang-tools-extra/clang-tidy/readability/ForwardUsageCheck.cpp
  clang-tools-extra/clang-tidy/readability/ForwardUsageCheck.h
  clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
  clang-tools-extra/docs/ReleaseNotes.rst
  clang-tools-extra/docs/clang-tidy/checks/list.rst
  clang-tools-extra/docs/clang-tidy/checks/readability/forward-usage.rst
  clang-tools-extra/test/clang-tidy/checkers/readability/forward-usage.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/readability/forward-usage.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/readability/forward-usage.cpp
@@ -0,0 +1,257 @@
+// RUN: %check_clang_tidy -std=c++11-or-later %s readability-forward-usage %t -- -- -fno-delayed-template-parsing
+
+namespace std {
+
+struct false_type {
+  static constexpr bool value = false;
+};
+
+struct true_type {
+  static constexpr bool value = true;
+};
+
+template <class T>
+struct is_lvalue_reference : false_type {};
+
+template <class T>
+struct is_lvalue_reference<T&> : true_type {};
+
+template <class T>
+struct remove_reference {
+  using type = T;
+};
+
+template <class T>
+struct remove_reference<T&> {
+  using type = T;
+};
+
+template <class T>
+struct remove_reference<T&&> {
+  using type = T;
+};
+
+template <class T>
+using remove_reference_t = typename remove_reference<T>::type;
+
+template <class T>
+constexpr T&& forward(typename std::remove_reference<T>::type& t) noexcept {
+  return static_cast<T&&>(t);
+}
+
+template <class T>
+constexpr T&& forward(typename std::remove_reference<T>::type&& t) noexcept {
+  static_assert(!std::is_lvalue_reference<T>::value, "Can't forward an rvalue as an lvalue.");
+  return static_cast<T&&>(t);
+}
+
+template <class T>
+constexpr typename std::remove_reference<T>::type&& move(T&& t) noexcept {
+  return static_cast<typename std::remove_reference<T>::type&&>(t);
+}
+
+}
+
+struct TestType {
+    int value = 0U;
+};
+
+struct TestTypeEx : TestType {
+};
+
+template<typename ...Args>
+void test(Args&&...) {}
+
+
+template<typename T>
+void testCorrectForward(T&& value) {
+    test(std::forward<T>(value));
+}
+
+template<typename ...Args>
+void testForwardVariadicTemplate(Args&& ...args) {
+    test(std::forward<Args>(args)...);
+}
+
+void callAll() {
+    TestType type1;
+    const TestType type2;
+    testCorrectForward(type1);
+    testCorrectForward(type2);
+    testCorrectForward(std::move(type1));
+    testCorrectForward(std::move(type2));
+    testForwardVariadicTemplate(type1, TestType(), std::move(type2));
+}
+
+void testExplicit(TestType& value) {
+    test(std::forward<TestType>(value));
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use 'std::move' instead of 'std::forward' here, as it more accurately reflects the intended purpose [readability-forward-usage]
+// CHECK-FIXES: {{^    }}test(std::move(value));{{$}}
+}
+
+void testExplicit2(const TestType& value) {
+    test(std::forward<const TestType>(value));
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use 'std::move' instead of 'std::forward' here, as it more accurately reflects the intended purpose [readability-forward-usage]
+// CHECK-FIXES: {{^    }}test(std::move(value));{{$}}
+}
+
+void testExplicit3(TestType value) {
+    test(std::forward<TestType>(value));
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use 'std::move' instead of 'std::forward' here, as it more accurately reflects the intended purpose [readability-forward-usage]
+// CHECK-FIXES: {{^    }}test(std::move(value));{{$}}
+}
+
+void testExplicit4(TestType&& value) {
+    test(std::forward<TestType>(value));
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use 'std::move' instead of 'std::forward' here, as it more accurately reflects the intended purpose [readability-forward-usage]
+// CHECK-FIXES: {{^    }}test(std::move(value));{{$}}
+}
+
+void testExplicit5(const TestType&& value) {
+    test(std::forward<const TestType>(value));
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use 'std::move' instead of 'std::forward' here, as it more accurately reflects the intended purpose [readability-forward-usage]
+// CHECK-FIXES: {{^    }}test(std::move(value));{{$}}
+}
+
+void testExplicit6(TestType&& value) {
+    test(std::forward<TestType&>(value));
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: remove redundant use of 'std::forward' as it is unnecessary in this context [readability-forward-usage]
+// CHECK-FIXES: {{^    }}test(value);{{$}}
+}
+
+void testExplicit7(TestType value) {
+    test(std::forward<TestType&>(value));
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: remove redundant use of 'std::forward' as it is unnecessary in this context [readability-forward-usage]
+// CHECK-FIXES: {{^    }}test(value);{{$}}
+}
+
+void testExplicit8(TestType value) {
+    test(std::forward<TestType&&>(value));
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use 'std::move' instead of 'std::forward' here, as it more accurately reflects the intended purpose [readability-forward-usage]
+// CHECK-FIXES: {{^    }}test(std::move(value));{{$}}
+}
+
+void testExplicit9(TestTypeEx value) {
+    test(std::forward<TestType>(value));
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: using 'std::forward' for type conversions from 'TestTypeEx' to 'TestType' is not recommended here, use 'static_cast' instead [readability-forward-usage]
+// CHECK-FIXES: {{^    }}test(static_cast<TestType &&>(value));{{$}}
+}
+
+void testExplicit10(TestTypeEx value) {
+    test(std::forward<TestType&>(value));
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: using 'std::forward' for type conversions from 'TestTypeEx' to 'TestType &' is not recommended here, use 'static_cast' instead [readability-forward-usage]
+// CHECK-FIXES: {{^    }}test(static_cast<TestType&>(value));{{$}}
+}
+
+void testExplicit11(TestTypeEx value) {
+    test(std::forward<TestType&&>(value));
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: using 'std::forward' for type conversions from 'TestTypeEx' to 'TestType &&' is not recommended here, use 'static_cast' instead [readability-forward-usage]
+// CHECK-FIXES: {{^    }}test(static_cast<TestType&&>(value));{{$}}
+}
+
+void testExplicit12(TestType value) {
+    test(std::forward<TestType>(std::move(value)));
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: remove redundant use of 'std::forward' as it is unnecessary in this context [readability-forward-usage]
+// CHECK-FIXES: {{^    }}test(std::move(value));{{$}}
+}
+
+void testExplicit13() {
+    test(std::forward<TestType>(TestType()));
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: remove redundant use of 'std::forward' as it is unnecessary in this context [readability-forward-usage]
+// CHECK-FIXES: {{^    }}test(TestType());{{$}}
+}
+
+TestType getTestType();
+
+void testExplicit14() {
+    test(std::forward<TestType>(getTestType()));
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: remove redundant use of 'std::forward' as it is unnecessary in this context [readability-forward-usage]
+// CHECK-FIXES: {{^    }}test(getTestType());{{$}}
+}
+
+#define FORWARD(x,y) std::forward<x>(y)
+#define FORWARD_FUNC std::forward
+#define TEMPLATE_ARG(x) <x>
+
+void testMacro(TestType value) {
+    test(FORWARD(TestType, value));
+    test(FORWARD_FUNC<TestType>(value));
+    test(std::forward TEMPLATE_ARG(TestType)(value));
+}
+
+template<typename T>
+struct Wrapper {};
+
+template<typename T>
+struct WrapperEx : Wrapper<T> {};
+
+template<typename T>
+struct TemplateClass
+{
+    void testTemplate(Wrapper<T> value) {
+        test(std::forward<Wrapper<T>>(value));
+// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use 'std::move' instead of 'std::forward' here, as it more accurately reflects the intended purpose [readability-forward-usage]
+// CHECK-FIXES: {{^        }}test(std::move(value));{{$}}
+    }
+};
+
+template<typename T>
+void testPartialTemplate(Wrapper<T> value) {
+    test(std::forward<Wrapper<T>>(value));
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use 'std::move' instead of 'std::forward' here, as it more accurately reflects the intended purpose [readability-forward-usage]
+// CHECK-FIXES: {{^    }}test(std::move(value));{{$}}
+
+    using Type = Wrapper<T>;
+    test(std::forward<Type>(value));
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use 'std::move' instead of 'std::forward' here, as it more accurately reflects the intended purpose [readability-forward-usage]
+// CHECK-FIXES: {{^    }}test(std::move(value));{{$}}
+
+}
+
+template<typename T>
+void testPartialTemplate(WrapperEx<T> value) {
+    test(std::forward<Wrapper<T>>(value));
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: using 'std::forward' for type conversions from 'WrapperEx<T>' to 'Wrapper<T>' is not recommended here, use 'static_cast' instead [readability-forward-usage]
+// CHECK-FIXES: {{^    }}test(static_cast<Wrapper<T> &&>(value));{{$}}
+}
+
+void testWrapper() {
+    TemplateClass<TestType>().testTemplate(Wrapper<TestType>());
+    testPartialTemplate(Wrapper<TestType>());
+}
+
+template<template <typename Type> class TemplateType>
+void testTemplateTemplate(TemplateType<TestType> value) {
+    test(std::forward<TemplateType<TestType>>(value));
+
+    using Type = TemplateType<TestType>;
+    test(std::forward<Type>(value));
+}
+
+template<int V>
+struct WrapperInt {};
+
+template<int V>
+WrapperInt<V>& getValueInt();
+
+template<int V>
+WrapperInt<V>&& getValueInt2();
+
+template<int ...Args>
+void CheckArgs() {
+    test(std::forward<WrapperInt<Args>&>(getValueInt<Args>())...);
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: remove redundant use of 'std::forward' as it is unnecessary in this context [readability-forward-usage]
+// CHECK-FIXES: {{^    }}test(getValueInt<Args>()...);{{$}}
+
+    test(std::forward<WrapperInt<Args>>(getValueInt<Args>())...);
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use 'std::move' instead of 'std::forward' here, as it more accurately reflects the intended purpose [readability-forward-usage]
+// CHECK-FIXES: {{^    }}test(std::move(getValueInt<Args>())...);{{$}}
+
+    test(std::forward<WrapperInt<Args>>(getValueInt2<Args>())...);
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use 'std::move' instead of 'std::forward' here, as it more accurately reflects the intended purpose [readability-forward-usage]
+// CHECK-FIXES: {{^    }}test(std::move(getValueInt2<Args>())...);{{$}}
+
+    test(std::forward<WrapperInt<Args>>(WrapperInt<Args>())...);
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use 'std::move' instead of 'std::forward' here, as it more accurately reflects the intended purpose [readability-forward-usage]
+// CHECK-FIXES: {{^    }}test(std::move(WrapperInt<Args>())...);{{$}}
+}
Index: clang-tools-extra/docs/clang-tidy/checks/readability/forward-usage.rst
===================================================================
--- /dev/null
+++ clang-tools-extra/docs/clang-tidy/checks/readability/forward-usage.rst
@@ -0,0 +1,120 @@
+.. title:: clang-tidy - readability-forward-usage
+
+readability-forward-usage
+=========================
+
+Suggests removing or replacing ``std::forward`` with ``std::move`` or
+``static_cast`` in cases where the template argument type is invariant and
+the call always produces the same result.
+
+Recognized applications
+-----------------------
+
+Check can detect different types of usages, as follows:
+
+Casting between different types
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The purpose of ``std::forward`` is to forward the value category of an argument,
+while preserving its constness and reference qualifiers. It is not intended to
+be used for type conversions, as that is outside the scope of its intended use.
+
+Using ``std::forward`` to convert between two types is a misuse because it can
+result in unexpected behavior, such as object slicing. Both the input and output
+types should be cv-equivalent, meaning that they represent the same fundamental
+type after removing all ``const``, ``volatile``, and reference qualifiers.
+
+Check will suggest usage of ``static_cast`` in this situation.
+
+.. code-block:: c++
+
+    struct Base {};
+    struct Derived : Base {};
+
+    void func(Base& base) {
+        doSomething(std::forward<Derived&>(base)); // Incorrect usage
+        doSomething(static_cast<Derived&>(base)); // Suggested usage
+    }
+
+
+Misusing ``std::forward`` as ``std::move``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+While it is technically possible to use ``std::forward`` to convert an `lvalue`
+to an `rvalue` when the resulting type is fixed, this is a misapplication
+because it can result in code that is difficult to read and understand.
+
+The C++ standard provides a better alternative in ``std::move``, which is
+intended specifically for converting an `lvalue` to an `rvalue`, and clearly
+communicates the intention of the developer.
+
+Check will suggest usage of ``std::move`` in this situation.
+
+.. code-block:: c++
+
+    struct Object {};
+
+    void func(Object& obj) {
+        doSomething(std::forward<Object&&>(obj)); // Incorrect usage
+        doSomething(std::move(obj)); // Suggested usage
+    }
+
+
+Redundant use of ``std::forward``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+When using ``std::forward`` to convert from an `lvalue` to another `lvalue`,
+or from an `rvalue` to another `rvalue`, the usage is redundant and should be
+avoided. In such cases, the ``std::forward`` call is unnecessary since the
+types are already equivalent, and it does not provide any additional value in
+preserving the value category of the argument.
+
+It is generally recommended to simply remove the ``std::forward`` call in
+these cases, as it does not have any effect on the result of the expression.
+
+Check will suggest removal of ``std::forward`` in this situation.
+
+.. code-block:: c++
+
+    struct Object {};
+
+    Object createObject();
+
+    void func(Object&& obj) {
+        doSomething(std::forward<Object&&>(createObject())); // Incorrect usage
+        doSomething(std::forward<Object>(createObject())); // Incorrect usage
+        doSomething(createObject()); // Suggested usage
+    }
+
+    void func(Object& obj) {
+        doSomething(std::forward<Object&>(obj)); // Incorrect usage
+        doSomething(obj); // Suggested usage
+    }
+
+
+Options
+-------
+
+.. option:: DisableTypeMismatchSuggestion
+
+    Disables suggestions for type mismatch situations. When this option is
+    enabled, the check treats situations with different types as if they were
+    cv-equal and won't suggest using ``static_cast``, but will suggest using
+    ``std::move`` or removing the call.
+    This option has a default value of `false`.
+
+.. option:: DisableRemoveSuggestion
+
+    Disables suggestions to remove redundant ``std::forward`` calls.
+    This option has a default value of `false`.
+
+.. option:: DisableMoveSuggestion
+
+    Disables suggestions to use ``std::move`` instead of ``std::forward`` for
+    `lvalue` to `rvalue` conversions.
+    This option has a default value of `false`.
+
+.. option:: IgnoreDependentExpresions
+
+    Ignore dependent expressions during analysis (like any templates dependent
+    code). This option has a default value of `false`.
Index: clang-tools-extra/docs/clang-tidy/checks/list.rst
===================================================================
--- clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -338,6 +338,7 @@
    `readability-delete-null-pointer <readability/delete-null-pointer.html>`_, "Yes"
    `readability-duplicate-include <readability/duplicate-include.html>`_, "Yes"
    `readability-else-after-return <readability/else-after-return.html>`_, "Yes"
+   `readability-forward-usage <readability/forward-usage.html>`_, "Yes"
    `readability-function-cognitive-complexity <readability/function-cognitive-complexity.html>`_,
    `readability-function-size <readability/function-size.html>`_,
    `readability-identifier-length <readability/identifier-length.html>`_,
Index: clang-tools-extra/docs/ReleaseNotes.rst
===================================================================
--- clang-tools-extra/docs/ReleaseNotes.rst
+++ clang-tools-extra/docs/ReleaseNotes.rst
@@ -120,6 +120,13 @@
   Checks that all implicit and explicit inline functions in header files are
   tagged with the ``LIBC_INLINE`` macro.
 
+- New :doc:`readability-forward-usage
+  <clang-tidy/checks/readability/forward-usage>` check.
+
+  Suggests removing or replacing ``std::forward`` with ``std::move`` or
+  ``static_cast`` in cases where the template argument type is invariant and
+  the call always produces the same result.
+
 New check aliases
 ^^^^^^^^^^^^^^^^^
 
Index: clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
===================================================================
--- clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
+++ clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
@@ -19,6 +19,7 @@
 #include "DeleteNullPointerCheck.h"
 #include "DuplicateIncludeCheck.h"
 #include "ElseAfterReturnCheck.h"
+#include "ForwardUsageCheck.h"
 #include "FunctionCognitiveComplexityCheck.h"
 #include "FunctionSizeCheck.h"
 #include "IdentifierLengthCheck.h"
@@ -78,6 +79,8 @@
         "readability-duplicate-include");
     CheckFactories.registerCheck<ElseAfterReturnCheck>(
         "readability-else-after-return");
+    CheckFactories.registerCheck<ForwardUsageCheck>(
+        "readability-forward-usage");
     CheckFactories.registerCheck<FunctionCognitiveComplexityCheck>(
         "readability-function-cognitive-complexity");
     CheckFactories.registerCheck<FunctionSizeCheck>(
Index: clang-tools-extra/clang-tidy/readability/ForwardUsageCheck.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/readability/ForwardUsageCheck.h
@@ -0,0 +1,39 @@
+//===--- ForwardUsageCheck.h - clang-tidy -----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_FORWARDUSAGECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_FORWARDUSAGECHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+
+/// Suggests removing or replacing std::forward with std::move or static_cast
+/// in cases where the template argument type is invariant and the call always
+/// produces the same result.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability/forward-usage.html
+class ForwardUsageCheck : public ClangTidyCheck {
+public:
+  ForwardUsageCheck(StringRef Name, ClangTidyContext *Context);
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  bool isLanguageVersionSupported(const LangOptions &LangOpts) const override;
+
+private:
+  const bool DisableTypeMismatchSuggestion;
+  const bool DisableRemoveSuggestion;
+  const bool DisableMoveSuggestion;
+  const bool IgnoreDependentExpresions;
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_FORWARDUSAGECHECK_H
Index: clang-tools-extra/clang-tidy/readability/ForwardUsageCheck.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/readability/ForwardUsageCheck.cpp
@@ -0,0 +1,192 @@
+//===--- ForwardUsageCheck.cpp - clang-tidy ------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ForwardUsageCheck.h"
+#include "../utils/ASTUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+namespace {
+
+AST_MATCHER_P(UnresolvedLookupExpr, hasOnyTemplateArgumentLoc,
+              ast_matchers::internal::Matcher<TemplateArgumentLoc>,
+              InnerMatcher) {
+  return Node.getNumTemplateArgs() == 1U &&
+         InnerMatcher.matches(Node.getTemplateArgs()[0], Finder, Builder);
+}
+
+AST_MATCHER(QualType, isFixedType) {
+  QualType CleanType = Node.getCanonicalType().getNonReferenceType();
+
+  if (CleanType->containsErrors())
+    return false;
+
+  if (CleanType->getDependence() == TypeDependenceScope::None)
+    return true;
+
+  const auto *TemplateStruct = CleanType->getAs<TemplateSpecializationType>();
+  if (TemplateStruct) {
+    return TemplateStruct->getTemplateName().getDependence() ==
+           TemplateNameDependence::None;
+  }
+
+  return false;
+}
+
+inline SourceRange getAnglesLoc(const Expr *MatchedCallee) {
+  if (const auto *DeclRefExprCallee =
+          llvm::dyn_cast_or_null<DeclRefExpr>(MatchedCallee))
+    return SourceRange(DeclRefExprCallee->getLAngleLoc(),
+                       DeclRefExprCallee->getRAngleLoc());
+
+  if (const auto *UnresolvedLookupExprCallee =
+          llvm::dyn_cast_or_null<UnresolvedLookupExpr>(MatchedCallee))
+    return SourceRange(UnresolvedLookupExprCallee->getLAngleLoc(),
+                       UnresolvedLookupExprCallee->getRAngleLoc());
+  return {};
+}
+
+} // namespace
+
+inline bool isDeducedType(const QualType &Type) {
+  return !Type->isSpecificBuiltinType(BuiltinType::Dependent);
+}
+
+inline bool hasXValueArgument(const CallExpr &Call) {
+  return Call.getArg(0U)->isXValue();
+}
+
+ForwardUsageCheck::ForwardUsageCheck(StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      DisableTypeMismatchSuggestion(
+          Options.get("DisableTypeMismatchSuggestion", false)),
+      DisableRemoveSuggestion(Options.get("DisableRemoveSuggestion", false)),
+      DisableMoveSuggestion(Options.get("DisableMoveSuggestion", false)),
+      IgnoreDependentExpresions(
+          Options.get("IgnoreDependentExpresions", false)) {}
+
+void ForwardUsageCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "DisableTypeMismatchSuggestion",
+                DisableTypeMismatchSuggestion);
+  Options.store(Opts, "DisableRemoveSuggestion", DisableRemoveSuggestion);
+  Options.store(Opts, "DisableMoveSuggestion", DisableMoveSuggestion);
+  Options.store(Opts, "IgnoreDependentExpresions", IgnoreDependentExpresions);
+}
+
+bool ForwardUsageCheck::isLanguageVersionSupported(
+    const LangOptions &LangOpts) const {
+  return LangOpts.CPlusPlus11;
+}
+
+void ForwardUsageCheck::registerMatchers(MatchFinder *Finder) {
+  const auto templateArgumentType =
+      templateArgumentLoc(hasTypeLoc(loc(qualType(isFixedType()).bind("T"))));
+
+  Finder->addMatcher(
+      traverse(
+          TK_IgnoreUnlessSpelledInSource,
+          callExpr(
+              unless(isExpansionInSystemHeader()), argumentCountIs(1U),
+              IgnoreDependentExpresions
+                  ? expr(unless(isInstantiationDependent()))
+                  : expr(),
+
+              unless(hasAncestor(functionDecl(isImplicit()))),
+
+              callee(
+                  expr(anyOf(declRefExpr(hasDeclaration(functionDecl(
+                                             hasName("::std::forward"))),
+                                         hasTemplateArgumentLoc(
+                                             0U, templateArgumentLoc(
+                                                     templateArgumentType))),
+                             unresolvedLookupExpr(
+                                 hasAnyDeclaration(
+                                     namedDecl(hasName("::std::forward"))),
+                                 hasOnyTemplateArgumentLoc(templateArgumentLoc(
+                                     templateArgumentType)))))
+                      .bind("callee")),
+              hasArgument(0U, expr(hasType(qualType().bind("arg")))))
+              .bind("call")),
+      this);
+}
+
+void ForwardUsageCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *MatchedCallee = Result.Nodes.getNodeAs<Expr>("callee");
+  if (MatchedCallee->getBeginLoc().isMacroID() ||
+      MatchedCallee->getEndLoc().isMacroID() ||
+      !utils::rangeCanBeFixed(MatchedCallee->getSourceRange(),
+                              Result.SourceManager))
+    return;
+
+  const auto *MatchedExpr = Result.Nodes.getNodeAs<CallExpr>("call");
+
+  const auto *MatchedTemplateType = Result.Nodes.getNodeAs<QualType>("T");
+  const auto *MatchedArgumentType = Result.Nodes.getNodeAs<QualType>("arg");
+
+  if (!DisableTypeMismatchSuggestion &&
+      MatchedTemplateType->getCanonicalType().getNonReferenceType() !=
+          MatchedArgumentType->getCanonicalType().getNonReferenceType() &&
+      isDeducedType(*MatchedArgumentType)) {
+
+    auto Diagnostic =
+        diag(MatchedExpr->getBeginLoc(),
+             "using 'std::forward' for type conversions from %0 to %1 is not "
+             "recommended here, use 'static_cast' instead");
+    Diagnostic << *MatchedArgumentType << *MatchedTemplateType;
+
+    const SourceRange AngleSourceRange = getAnglesLoc(MatchedCallee);
+    if (AngleSourceRange.isInvalid())
+      return;
+
+    if (!MatchedTemplateType->getCanonicalType()->isReferenceType()) {
+      Diagnostic << FixItHint::CreateReplacement(
+          SourceRange(AngleSourceRange.getEnd(), AngleSourceRange.getEnd()),
+          " &&>");
+    }
+
+    Diagnostic << FixItHint::CreateReplacement(
+        SourceRange(MatchedCallee->getBeginLoc(), AngleSourceRange.getBegin()),
+        "static_cast<");
+    return;
+  }
+
+  const bool LValueReferenceTemplateType =
+      (*MatchedTemplateType)->isLValueReferenceType();
+  const bool XValueArgument =
+      hasXValueArgument(*MatchedExpr) && isDeducedType(*MatchedArgumentType);
+
+  if (!LValueReferenceTemplateType && !XValueArgument) {
+    if (DisableMoveSuggestion)
+      return;
+
+    diag(MatchedExpr->getBeginLoc(),
+         "use 'std::move' instead of 'std::forward' here, as it more "
+         "accurately reflects the intended purpose")
+        << FixItHint::CreateReplacement(MatchedCallee->getSourceRange(),
+                                        "std::move");
+    return;
+  }
+
+  if (DisableRemoveSuggestion)
+    return;
+
+  const SourceLocation BeforeArgBegin =
+      MatchedExpr->getArg(0U)->getBeginLoc().getLocWithOffset(-1);
+  diag(MatchedExpr->getBeginLoc(), "remove redundant use of 'std::forward' as "
+                                   "it is unnecessary in this context")
+      << FixItHint::CreateRemoval(SourceRange(MatchedExpr->getRParenLoc(),
+                                              MatchedExpr->getRParenLoc()))
+      << FixItHint::CreateRemoval(
+             SourceRange(MatchedExpr->getBeginLoc(), BeforeArgBegin));
+}
+
+} // namespace clang::tidy::readability
Index: clang-tools-extra/clang-tidy/readability/CMakeLists.txt
===================================================================
--- clang-tools-extra/clang-tidy/readability/CMakeLists.txt
+++ clang-tools-extra/clang-tidy/readability/CMakeLists.txt
@@ -14,6 +14,7 @@
   DeleteNullPointerCheck.cpp
   DuplicateIncludeCheck.cpp
   ElseAfterReturnCheck.cpp
+  ForwardUsageCheck.cpp
   FunctionCognitiveComplexityCheck.cpp
   FunctionSizeCheck.cpp
   IdentifierLengthCheck.cpp
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to