5chmidti updated this revision to Diff 454287.
5chmidti added a comment.

- address all comments
- minor renames of variables/functions
- add missing & when accessing OptTokens value
- rebase onto current HEAD
- fix llvm::Optional member deprecation warnings
- add support to match when the parameter type explicitly instantiaties itself 
(i.e. const A<T>& instead of const A& for template <typename T> struct A {...};)


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D128861

Files:
  clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt
  clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
  
clang-tools-extra/clang-tidy/cppcoreguidelines/SymmetricBinaryOperatorCheck.cpp
  clang-tools-extra/clang-tidy/cppcoreguidelines/SymmetricBinaryOperatorCheck.h
  clang-tools-extra/docs/ReleaseNotes.rst
  
clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/symmetric-binary-operator.rst
  clang-tools-extra/docs/clang-tidy/checks/list.rst
  
clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/symmetric-binary-operator.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/symmetric-binary-operator.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/symmetric-binary-operator.cpp
@@ -0,0 +1,505 @@
+// RUN: %check_clang_tidy -std=c++20 %s cppcoreguidelines-symmetric-binary-operator %t
+
+namespace std {
+class string {
+public:
+  friend bool operator!=(const string &, const string &) { return true; }
+  friend bool operator<(const string &, const string &) { return true; }
+};
+template <typename T>
+class vector {
+public:
+  int size() const;
+  const T &operator[](int) const;
+  friend bool operator==(const vector &, const vector &) = default;
+};
+} // namespace std
+
+using size_t = unsigned long long;
+
+namespace test_base {
+struct A {
+  int X;
+  bool operator==(const A &) const = default;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+  // CHECK-FIXES: friend bool operator==(const A&, const A&) = default;
+};
+
+struct A2 {
+  int X;
+  bool operator==(const A2 &rhs) const = default;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+  // CHECK-FIXES: friend bool operator==(const A2&, const A2&) = default;
+};
+
+struct B {
+  int X;
+  friend bool operator==(const B &, const B &) = default;
+};
+
+template <typename T>
+struct C {
+  int X;
+  bool operator==(const C &) const = default;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+  // CHECK-FIXES: friend bool operator==(const C&, const C&) = default;
+};
+
+template <typename T>
+struct D {
+  int X;
+  friend bool operator==(const D &, const D &) = default;
+};
+
+struct E {
+  int X;
+  bool operator==(const E &) const = default;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+  // CHECK-FIXES: friend bool operator==(const E&, const E&) = default;
+};
+
+struct F {
+  int X;
+  friend bool operator==(const F &, const F &) = default;
+};
+
+template <typename T>
+struct G {
+  int X;
+  bool operator==(const G &) const = default;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+  // CHECK-FIXES: friend bool operator==(const G&, const G&) = default;
+};
+
+template <typename T>
+struct H {
+  int X;
+  friend bool operator==(const H &, const H &) = default;
+};
+
+template <typename T>
+struct I {
+  int X;
+  bool operator==(const I<T>&) const = default;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+  // CHECK-FIXES: friend bool operator==(const I&, const I&) = default;
+};
+
+template <typename ...T>
+struct J {
+  int X;
+  bool operator==(const J<T...>&) const = default;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+  // CHECK-FIXES: friend bool operator==(const J&, const J&) = default;
+};
+
+template <typename T, int N>
+struct K {
+  int X;
+  bool operator==(const K<T,N>&) const = default;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+  // CHECK-FIXES: friend bool operator==(const K&, const K&) = default;
+};
+} // namespace test_base
+
+namespace test_constexpr {
+struct A {
+  int X;
+  constexpr bool operator==(const A &) const = default;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+  // CHECK-FIXES: friend constexpr bool operator==(const A&, const A&) = default;
+};
+
+struct B {
+  int X;
+  friend constexpr bool operator==(const B &, const B &) = default;
+};
+
+template <typename T>
+struct C {
+  int X;
+  constexpr bool operator==(const C &) const = default;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+  // CHECK-FIXES: friend constexpr bool operator==(const C&, const C&) = default;
+};
+
+template <typename T>
+struct D {
+  int X;
+  friend constexpr bool operator==(const D &, const D &) = default;
+};
+
+struct E {
+  int X;
+  constexpr bool operator==(const E &) const = default;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+  // CHECK-FIXES: friend constexpr bool operator==(const E&, const E&) = default;
+};
+
+struct F {
+  int X;
+  friend constexpr bool operator==(const F &, const F &) = default;
+};
+
+template <typename T>
+struct G {
+  int X;
+  constexpr bool operator==(const G &) const = default;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+  // CHECK-FIXES: friend constexpr bool operator==(const G&, const G&) = default;
+};
+
+template <typename T>
+struct H {
+  int X;
+  friend constexpr bool operator==(const H &, const H &) = default;
+};
+} // namespace test_constexpr
+
+namespace test_trailing_return_type {
+struct A {
+  int X;
+  auto operator==(const A &) const -> bool = default;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+  // CHECK-FIXES: friend auto operator==(const A&, const A&) -> bool = default;
+};
+
+struct B {
+  int X;
+  friend auto operator==(const B &, const B &) -> bool = default;
+};
+
+template <typename T>
+struct C {
+  int X;
+  auto operator==(const C &) const -> bool = default;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+  // CHECK-FIXES: friend auto operator==(const C&, const C&) -> bool = default;
+};
+
+template <typename T>
+struct D {
+  int X;
+  friend auto operator==(const D &, const D &) -> bool = default;
+};
+
+struct E {
+  int X;
+  auto operator==(const E &) const -> bool = default;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+  // CHECK-FIXES: friend auto operator==(const E&, const E&) -> bool = default;
+};
+
+struct F {
+  int X;
+  friend auto operator==(const F &, const F &) -> bool = default;
+};
+
+template <typename T>
+struct G {
+  int X;
+  auto operator==(const G &) const -> bool = default;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+  // CHECK-FIXES: friend auto operator==(const G&, const G&) -> bool = default;
+};
+
+template <typename T>
+struct H {
+  int X;
+  friend auto operator==(const H &, const H &) -> bool = default;
+};
+} // namespace test_trailing_return_type
+
+namespace non_default {
+struct A {
+  int X;
+  bool operator==(const A &rhs) const {
+    return X == rhs.X;
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+};
+
+template <typename T>
+struct C {
+  int X;
+  bool operator==(const C &rhs) const {
+    return X == rhs.X;
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+};
+} // namespace non_default
+
+namespace non_const {
+struct A {
+  int X;
+  bool operator==(const A &rhs) {
+    return X == rhs.X;
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+};
+
+template <typename T>
+struct C {
+  int X;
+  bool operator==(const C &rhs) {
+    return X == rhs.X;
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+};
+} // namespace non_const
+
+namespace out_of_class_definition {
+struct A {
+  int X;
+  bool operator==(const A &rhs) const;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+};
+
+bool A::operator==(const A &rhs) const {
+  return X == rhs.X;
+}
+
+struct B {
+  int X;
+  bool operator==(const B &rhs) const;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+};
+
+template <typename T>
+struct C {
+  int X;
+  bool operator==(const C &rhs) const;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+};
+
+template <typename T>
+bool C<T>::operator==(const C<T> &rhs) const {
+  return X == rhs.X;
+}
+} // namespace out_of_class_definition
+
+bool out_of_class_definition::B::operator==(const B &rhs) const {
+  return X == rhs.X;
+}
+
+namespace templates_no_match {
+template <typename T>
+struct A {
+  bool operator==(const T &);
+};
+
+struct B {
+  template <typename T>
+  bool operator==(const T &);
+};
+
+template <typename T>
+struct C {
+  template <typename W>
+  bool operator==(const C<W> &);
+  bool operator!=(const C<int> &);
+};
+
+} // namespace templates_no_match
+
+namespace test_base_definition_not_visible {
+struct A {
+  int X;
+  bool operator==(const A &) const;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+};
+
+struct A2 {
+  int X;
+  bool operator==(const A2 &rhs) const;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+};
+
+struct B {
+  int X;
+  friend bool operator==(const B &, const B &);
+};
+
+template <typename T>
+struct C {
+  int X;
+  bool operator==(const C &) const;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+};
+
+template <typename T>
+struct D {
+  int X;
+  friend bool operator==(const D &, const D &);
+};
+
+struct E {
+  int X;
+  bool operator==(const E &) const;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+};
+
+struct F {
+  int X;
+  friend bool operator==(const F &, const F &);
+};
+
+template <typename T>
+struct G {
+  int X;
+  bool operator==(const G &) const;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+};
+
+template <typename T>
+struct H {
+  int X;
+  friend bool operator==(const H &, const H &);
+};
+} // namespace test_base_definition_not_visible
+
+namespace additional_operators {
+struct A {
+  bool operator<(const A &);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+  bool operator+(const A &);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+  bool operator<<(const A &);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+  bool operator&&(const A &);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+  bool operator,(const A &);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+
+  bool operator<(const int &);
+  bool operator+(const int &);
+  bool operator<<(const int &);
+  bool operator&&(const int &);
+  bool operator,(const int &);
+};
+
+template <typename T>
+struct B {
+  bool operator<(const B &);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+  bool operator+(const B &);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+  bool operator<<(const B &);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+  bool operator&&(const B &);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+  bool operator,(const B &);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+
+  bool operator<(const int &);
+  bool operator+(const int &);
+  bool operator<<(const int &);
+  bool operator&&(const int &);
+  bool operator,(const int &);
+};
+
+} // namespace additional_operators
+
+namespace test_noexcept {
+
+struct A {
+  int X;
+  bool operator==(const A &) const noexcept = default;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+  // CHECK-FIXES: friend bool operator==(const A&, const A&) noexcept = default;
+};
+
+struct A2 {
+  int X;
+  bool operator==(const A2 &rhs) const noexcept = default;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+  // CHECK-FIXES: friend bool operator==(const A2&, const A2&) noexcept = default;
+};
+} // namespace test_noexcept
+
+namespace llvm_omp_testcase {
+template <typename T>
+using SetVector = ::std::vector<T>;
+
+enum class ChangeStatus {
+  CHANGED,
+  UNCHANGED,
+};
+
+ChangeStatus operator|(ChangeStatus l, ChangeStatus r);
+ChangeStatus &operator|=(ChangeStatus &l, ChangeStatus r);
+ChangeStatus operator&(ChangeStatus l, ChangeStatus r);
+ChangeStatus &operator&=(ChangeStatus &l, ChangeStatus r);
+
+struct AbstractState {
+  virtual ~AbstractState() = default;
+};
+
+template <typename base_ty, base_ty BestState, base_ty WorstState>
+struct IntegerStateBase : public AbstractState {
+  using base_t = base_ty;
+
+  IntegerStateBase() = default;
+  IntegerStateBase(base_t Assumed) : Assumed(Assumed) {}
+
+  static constexpr base_t getBestState() { return BestState; }
+  static constexpr base_t getBestState(const IntegerStateBase &) {
+    return getBestState();
+  }
+
+  static constexpr base_t getWorstState() { return WorstState; }
+  static constexpr base_t getWorstState(const IntegerStateBase &) {
+    return getWorstState();
+  }
+
+  base_t getKnown() const { return Known; }
+
+  base_t getAssumed() const { return Assumed; }
+
+  bool
+  operator==(const IntegerStateBase<base_t, BestState, WorstState> &R) const {
+    return this->getAssumed() == R.getAssumed() &&
+           this->getKnown() == R.getKnown();
+  }
+  // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+
+  bool
+  operator!=(const IntegerStateBase<base_t, BestState, WorstState> &R) const {
+    return !(*this == R);
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+
+protected:
+  virtual void handleNewAssumedValue(base_t Value) = 0;
+
+  virtual void handleNewKnownValue(base_t Value) = 0;
+
+  base_t Known = getWorstState();
+
+  base_t Assumed = getBestState();
+};
+
+struct BooleanState : public IntegerStateBase<bool, true, false> {
+  using super = IntegerStateBase<bool, true, false>;
+  using base_t = IntegerStateBase::base_t;
+
+  BooleanState() = default;
+  BooleanState(base_t Assumed) : super(Assumed) {}
+};
+
+template <typename Ty, bool InsertInvalidates = true>
+struct BooleanStateWithSetVector : public BooleanState {
+  bool operator==(const BooleanStateWithSetVector &RHS) const {
+    return BooleanState::operator==(RHS) && Set == RHS.Set;
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+  bool operator!=(const BooleanStateWithSetVector &RHS) const {
+    return !(*this == RHS);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: symmetric binary operator should be a friend or free operator function [cppcoreguidelines-symmetric-binary-operator]
+
+private:
+  SetVector<Ty> Set;
+};
+
+} // namespace llvm_omp_testcase
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
@@ -198,6 +198,7 @@
    `cppcoreguidelines-pro-type-vararg <cppcoreguidelines/pro-type-vararg.html>`_,
    `cppcoreguidelines-slicing <cppcoreguidelines/slicing.html>`_,
    `cppcoreguidelines-special-member-functions <cppcoreguidelines/special-member-functions.html>`_,
+   `cppcoreguidelines-symmetric-binary-operator <cppcoreguidelines/symmetric-binary-operator.html>`_, "Yes"
    `cppcoreguidelines-virtual-class-destructor <cppcoreguidelines/virtual-class-destructor.html>`_, "Yes"
    `darwin-avoid-spinlock <darwin/avoid-spinlock.html>`_,
    `darwin-dispatch-once-nonstatic <darwin/dispatch-once-nonstatic.html>`_, "Yes"
Index: clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/symmetric-binary-operator.rst
===================================================================
--- /dev/null
+++ clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/symmetric-binary-operator.rst
@@ -0,0 +1,35 @@
+.. title:: clang-tidy - cppcoreguidelines-symmetric-binary-operator
+
+cppcoreguidelines-symmetric-binary-operator
+===========================================
+
+Warns that symmetric binary operators should be friend or free functions.
+
+Operators are considered symmetric if the type of lhs and rhs are the same as the record they are declared in.
+
+Given a simple struct with member operators
+(incomplete set of operators, for demonstration purposes only)
+
+.. code-block:: c++
+
+   struct A {
+        int X;
+        // Member-operator that is symmetric, defaulted and a comparison operator
+        bool operator==(const A &) const = default; // Warn + fix
+        A operator+(const A &rhs) const { return A{X + rhs.X}; } // Warn
+    };
+
+the check matches both member operators ``operator==`` and ``operator+`` and
+warns on both occasions that it is a symmetric binary operator that should be a friend or free function.
+Additionally, a fix is created for symmetric comparison operators, ``operator==`` in the example,
+that rewrites the operator to a friend operator function
+
+.. code-block:: c++
+
+   struct A {
+        int X;
+        friend bool operator==(const A &, const A &) = default;  // Rewritten operator
+        A operator+(const A &rhs) const { return A{X + rhs.X}; }
+    };
+
+The check is based on `C++ Core Guidelines C.161 <https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#c161-use-non-member-functions-for-symmetric-operators>`
Index: clang-tools-extra/docs/ReleaseNotes.rst
===================================================================
--- clang-tools-extra/docs/ReleaseNotes.rst
+++ clang-tools-extra/docs/ReleaseNotes.rst
@@ -104,6 +104,10 @@
 
   Warns when a struct or class uses const or reference (lvalue or rvalue) data members.
 
+- New :doc:`cppcoreguidelines-symmetric-binary-operator <clang-tidy/checks/cppcoreguidelines/symmetric-binary-operator>` check.
+
+  Warns that symmetric binary operators should be friend or free functions.
+
 New check aliases
 ^^^^^^^^^^^^^^^^^
 
@@ -120,7 +124,7 @@
   <clang-tidy/checks/bugprone/signal-handler>` check. Partial
   support for C++14 signal handler rules was added. Bug report generation was
   improved.
-  
+
 - Improved `modernize-use-emplace <clang-tidy/checks/modernize/use-emplace.html>`_ check.
 
   The check now supports detecting inefficient invocations of ``push`` and
Index: clang-tools-extra/clang-tidy/cppcoreguidelines/SymmetricBinaryOperatorCheck.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/cppcoreguidelines/SymmetricBinaryOperatorCheck.h
@@ -0,0 +1,38 @@
+//===--- SymmetricBinaryOperatorCheck.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_CPPCOREGUIDELINES_SYMMETRICBINARYOPERATORCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_SYMMETRICBINARYOPERATORCHECK_H
+
+#include "../utils/TransformerClangTidyCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+/// Finds operators that are symmetric with respect to the type of their
+/// parameters. Warns that such operators should be friend functions or free
+/// functions. Generates fixes to change defaulted comparison operators to
+/// defaulted friend operators. See also C.161.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines/symmetric-binary-operator.html
+class SymmetricBinaryOperatorCheck : public utils::TransformerClangTidyCheck {
+public:
+  SymmetricBinaryOperatorCheck(StringRef Name, ClangTidyContext *Context);
+
+  bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+    return LangOpts.CPlusPlus20;
+  }
+};
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_SYMMETRICBINARYOPERATORCHECK_H
Index: clang-tools-extra/clang-tidy/cppcoreguidelines/SymmetricBinaryOperatorCheck.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/cppcoreguidelines/SymmetricBinaryOperatorCheck.cpp
@@ -0,0 +1,163 @@
+//===--- SymmetricBinaryOperatorCheck.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 "SymmetricBinaryOperatorCheck.h"
+#include "../utils/LexerUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Basic/OperatorKinds.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Tooling/Transformer/RangeSelector.h"
+#include "clang/Tooling/Transformer/RewriteRule.h"
+#include "clang/Tooling/Transformer/Stencil.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/ErrorHandling.h"
+#include <bitset>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+using namespace transformer;
+
+bool isPotentialSymmetricBinaryOperator(OverloadedOperatorKind Kind) {
+  return llvm::is_contained(
+      std::initializer_list<OverloadedOperatorKind>{
+#define OVERLOADED_OPERATOR_IS_BINARY_true(Name) OO_##Name,
+#define OVERLOADED_OPERATOR_IS_BINARY_false(Name)
+
+#define OVERLOADED_OPERATOR_IS_ASSIGNMENT_true(Name, Binary)
+#define OVERLOADED_OPERATOR_IS_ASSIGNMENT_false(Name, Binary)                  \
+  OVERLOADED_OPERATOR_IS_BINARY_##Binary(Name)
+
+#define OVERLOADED_OPERATOR(Name, Spelling, Token, Unary, Binary, MemberOnly)  \
+  OVERLOADED_OPERATOR_IS_ASSIGNMENT_##MemberOnly(Name, Binary)
+#define OVERLOADED_OPERATOR_MULTI(Name, Spelling, Unary, Binary, MemberOnly)
+
+#include "clang/Basic/OperatorKinds.def"
+      },
+      Kind);
+}
+
+bool isComparisonOperator(OverloadedOperatorKind Kind) {
+  std::bitset<NUM_OVERLOADED_OPERATORS> BitSetCompOps{};
+  BitSetCompOps.set(OO_Less);
+  BitSetCompOps.set(OO_Greater);
+  BitSetCompOps.set(OO_EqualEqual);
+  BitSetCompOps.set(OO_ExclaimEqual);
+  BitSetCompOps.set(OO_LessEqual);
+  BitSetCompOps.set(OO_GreaterEqual);
+  BitSetCompOps.set(OO_Spaceship);
+  BitSetCompOps.set(OO_AmpAmp);
+  BitSetCompOps.set(OO_PipePipe);
+  return BitSetCompOps[Kind];
+}
+
+AST_MATCHER(CXXMethodDecl, isPotentialSymmetricBinaryOperator) {
+  return isPotentialSymmetricBinaryOperator(Node.getOverloadedOperator());
+}
+
+AST_MATCHER(CXXMethodDecl, isComparisonOperator) {
+  return isComparisonOperator(Node.getOverloadedOperator());
+}
+
+static DynTypedNode getNode(const BoundNodes &Nodes, StringRef ID) {
+  auto &NodesMap = Nodes.getMap();
+  auto It = NodesMap.find(ID);
+  assert(It != NodesMap.end() && "getNode called with unbound ID");
+  return It->second;
+}
+
+static RangeSelector constSpec(std::string ID) {
+  return [ID = std::move(ID)](const MatchFinder::MatchResult &Result)
+             -> Expected<CharSourceRange> {
+    DynTypedNode Node = getNode(Result.Nodes, ID);
+    if (const auto *Method = Node.get<CXXMethodDecl>()) {
+      const SourceLocation ParensEndLoc =
+          Method->getFunctionTypeLoc().getParensRange().getEnd();
+
+      const auto GetNextToken = [&](const SourceLocation Loc) {
+        return utils::lexer::findNextTokenSkippingComments(
+            Loc, *Result.SourceManager, Result.Context->getLangOpts());
+      };
+
+      llvm::Optional<Token> OptToken{GetNextToken(ParensEndLoc)};
+
+      const auto IsConstToken = [](const Token &Tok) {
+        return Tok.is(tok::TokenKind::kw_const) ||
+               (Tok.is(tok::TokenKind::raw_identifier) &&
+                Tok.getRawIdentifier().equals("const"));
+      };
+
+      while (OptToken.has_value() && !IsConstToken(OptToken.value()) &&
+             !OptToken.value().isOneOf(tok::TokenKind::l_brace,
+                                       tok::TokenKind::kw_default,
+                                       tok::TokenKind::semi)) {
+        OptToken = GetNextToken(OptToken.value().getLocation());
+      }
+
+      if (OptToken.has_value() && IsConstToken(OptToken.value())) {
+        const auto &Token = OptToken.value();
+        return CharSourceRange::getCharRange(Token.getLocation(),
+                                             Token.getEndLoc());
+      }
+      // required on defaulted comparison operator members which is the only
+      // bound node constSpec gets called on
+      llvm_unreachable(
+          "constSpec invalid argument: member function is not const qualified");
+    }
+    llvm_unreachable("constSpec invalid argument: expected ID to be bound "
+                     "to a CXXMethodDecl");
+  };
+}
+
+static RewriteRuleWith<std::string> makeRewriteRule() {
+  const auto ParameterOfOwnType =
+      parmVarDecl(anyOf(hasType(qualType(references(
+                            cxxRecordDecl(equalsBoundNode("record"))))),
+                        hasType(injectedClassNameType())))
+          .bind("param");
+
+  const auto IsBinaryOperator =
+      cxxMethodDecl(isPotentialSymmetricBinaryOperator(),
+                    hasParameter(0, ParameterOfOwnType));
+
+  // Using hasParent instead of ofClass because we want to match declarations
+  // within the CXXRecord and not an outside definition
+  const auto BaseMatch =
+      cxxMethodDecl(hasParent(cxxRecordDecl().bind("record")), IsBinaryOperator,
+                    unless(hasAncestor(functionDecl())))
+          .bind("op");
+  const auto MatchDefaultedComparisonOperator =
+      cxxMethodDecl(BaseMatch, isDefaulted(), isComparisonOperator());
+
+  const auto AddFriendToFunctionDecl = insertBefore(node("op"), cat("friend "));
+  const auto RemoveConst = remove(constSpec("op"));
+  const auto AddSecondParameterToDeclaration =
+      changeTo(node("param"),
+               cat("const ", name("record"), "&, const ", name("record"), "&"));
+
+  const auto WarningMessage = cat(
+      "symmetric binary operator should be a friend or free operator function");
+
+  return applyFirst(
+      {makeRule(MatchDefaultedComparisonOperator,
+                {AddFriendToFunctionDecl, RemoveConst,
+                 AddSecondParameterToDeclaration},
+                WarningMessage),
+       makeRule(BaseMatch, noopEdit(node("op")), WarningMessage)});
+}
+
+SymmetricBinaryOperatorCheck::SymmetricBinaryOperatorCheck(
+    StringRef Name, ClangTidyContext *Context)
+    : TransformerClangTidyCheck(makeRewriteRule(), Name, Context) {}
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
Index: clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
===================================================================
--- clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
+++ clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
@@ -36,6 +36,7 @@
 #include "ProTypeVarargCheck.h"
 #include "SlicingCheck.h"
 #include "SpecialMemberFunctionsCheck.h"
+#include "SymmetricBinaryOperatorCheck.h"
 #include "VirtualClassDestructorCheck.h"
 
 namespace clang {
@@ -98,6 +99,8 @@
     CheckFactories.registerCheck<SlicingCheck>("cppcoreguidelines-slicing");
     CheckFactories.registerCheck<misc::UnconventionalAssignOperatorCheck>(
         "cppcoreguidelines-c-copy-assignment-signature");
+    CheckFactories.registerCheck<SymmetricBinaryOperatorCheck>(
+        "cppcoreguidelines-symmetric-binary-operator");
     CheckFactories.registerCheck<VirtualClassDestructorCheck>(
         "cppcoreguidelines-virtual-class-destructor");
   }
Index: clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt
===================================================================
--- clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt
+++ clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt
@@ -27,6 +27,7 @@
   ProTypeVarargCheck.cpp
   SlicingCheck.cpp
   SpecialMemberFunctionsCheck.cpp
+  SymmetricBinaryOperatorCheck.cpp
   VirtualClassDestructorCheck.cpp
 
   LINK_LIBS
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to