diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt b/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt @@ -26,6 +26,7 @@ ProTypeVarargCheck.cpp SlicingCheck.cpp SpecialMemberFunctionsCheck.cpp + SymmetricBinaryOperatorCheck.cpp VirtualClassDestructorCheck.cpp LINK_LIBS diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp @@ -35,6 +35,7 @@ #include "ProTypeVarargCheck.h" #include "SlicingCheck.h" #include "SpecialMemberFunctionsCheck.h" +#include "SymmetricBinaryOperatorCheck.h" #include "VirtualClassDestructorCheck.h" namespace clang { @@ -95,6 +96,8 @@ CheckFactories.registerCheck("cppcoreguidelines-slicing"); CheckFactories.registerCheck( "cppcoreguidelines-c-copy-assignment-signature"); + CheckFactories.registerCheck( + "cppcoreguidelines-symmetric-binary-operator"); CheckFactories.registerCheck( "cppcoreguidelines-virtual-class-destructor"); } diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/SymmetricBinaryOperatorCheck.h b/clang-tools-extra/clang-tidy/cppcoreguidelines/SymmetricBinaryOperatorCheck.h new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/SymmetricBinaryOperatorCheck.h @@ -0,0 +1,36 @@ +//===--- 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; +}; + +} // namespace cppcoreguidelines +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_SYMMETRICBINARYOPERATORCHECK_H diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/SymmetricBinaryOperatorCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/SymmetricBinaryOperatorCheck.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/SymmetricBinaryOperatorCheck.cpp @@ -0,0 +1,184 @@ +//===--- 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/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" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace cppcoreguidelines { +using transformer::applyFirst; +using transformer::cat; +using transformer::changeTo; +using transformer::insertBefore; +using transformer::makeRule; +using transformer::name; +using transformer::node; +using transformer::noopEdit; +using transformer::RangeSelector; +using transformer::remove; +using transformer::RewriteRule; + +bool isPotentialBinaryOperator(OverloadedOperatorKind Kind) { + return llvm::is_contained( + std::initializer_list{ +#define OVERLOADED_OPERATOR_true(Name) OO_##Name, +#define OVERLOADED_OPERATOR_false(Name) + +#define OVERLOADED_OPERATOR_IS_ASSIGNMENT_true(Name, Binary) +#define OVERLOADED_OPERATOR_IS_ASSIGNMENT_false(Name, Binary) \ + OVERLOADED_OPERATOR_##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) { + return llvm::is_contained( + std::initializer_list{ + OO_Less, + OO_Greater, + OO_EqualEqual, + OO_ExclaimEqual, + OO_LessEqual, + OO_GreaterEqual, + OO_Spaceship, + OO_AmpAmp, + OO_PipePipe, + }, + Kind); +} + +AST_MATCHER(CXXMethodDecl, isPotentialBinaryOperator) { + return isPotentialBinaryOperator(Node.getOverloadedOperator()); +} + +AST_MATCHER(CXXMethodDecl, isComparisonOperator) { + return isComparisonOperator(Node.getOverloadedOperator()); +} + +static llvm::Error invalidArgumentError(Twine Message) { + return llvm::make_error(llvm::errc::invalid_argument, + Message); +} + +static Expected getNode(const BoundNodes &Nodes, StringRef ID) { + auto &NodesMap = Nodes.getMap(); + auto It = NodesMap.find(ID); + if (It == NodesMap.end()) + return invalidArgumentError("ID not bound: " + ID); + return It->second; +} + +static RangeSelector constSpec(std::string ID) { + return [ID](const MatchFinder::MatchResult &Result) + -> Expected { + Expected Node = getNode(Result.Nodes, ID); + + if (!Node) + return Node.takeError(); + + if (const auto *Method = Node->get()) { + 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 OptToken{GetNextToken(ParensEndLoc)}; + + const auto IsConstToken = [](Token &Tok) { + return Tok.is(tok::TokenKind::kw_const) || + (Tok.is(tok::TokenKind::raw_identifier) && + Tok.getRawIdentifier().equals("const")); + }; + + for (; OptToken.hasValue() && !IsConstToken(OptToken.getValue()) && + !OptToken.getValue().isOneOf(tok::TokenKind::l_brace, + tok::TokenKind::kw_default, + tok::TokenKind::semi); + OptToken = GetNextToken(OptToken.getValue().getLocation())) { + } + + if (OptToken.hasValue() && IsConstToken(OptToken.getValue())) { + const auto Token = OptToken.getValue(); + return CharSourceRange::getCharRange(Token.getLocation(), + Token.getEndLoc()); + } + // This error is unreachable within this check. constSpec is only called + // on defaulted symmetric binary operators, which are required to be const + // specified. + return invalidArgumentError( + "constSpec invalid argument: member function is not const qualified"); + }; + return invalidArgumentError( + "constSpec invalid argument: not a CXXMethodDecl"); + }; +} + +static transformer::RewriteRuleWith makeRewriteRule() { + const auto Parameter = + parmVarDecl(hasType(qualType(references(cxxRecordDecl().bind("record"))))) + .bind("param"); + + const auto IsBinaryOperator = + cxxMethodDecl(isPotentialBinaryOperator(), hasParameter(0, Parameter)); + + // Using hasParent instead of ofClass because we want to match declarations + // within the CXXRecord and not an outside definition + const auto BaseMatch = + cxxMethodDecl(IsBinaryOperator, unless(hasAncestor(functionDecl())), + hasParent(cxxRecordDecl(equalsBoundNode("record")))) + .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) {} + +bool SymmetricBinaryOperatorCheck::isLanguageVersionSupported( + const LangOptions &LangOpts) const { + return LangOpts.CPlusPlus20; +} +} // namespace cppcoreguidelines +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -128,6 +128,10 @@ Warns when the code is unwrapping a `std::optional`, `absl::optional`, or `base::Optional` object without assuring that it contains a value. +- New :doc:`cppcoreguidelines-symmetric-binary-operator ` check. + + Warns that symmetric binary operators should be friend or free functions. + - New :doc:`misc-confusable-identifiers ` check. Detects confusable Unicode identifiers. diff --git a/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/symmetric-binary-operator.rst b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/symmetric-binary-operator.rst new file mode 100644 --- /dev/null +++ b/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 ` diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -196,6 +196,7 @@ `cppcoreguidelines-pro-type-vararg `_, `cppcoreguidelines-slicing `_, `cppcoreguidelines-special-member-functions `_, + `cppcoreguidelines-symmetric-binary-operator `_, "Yes" `cppcoreguidelines-virtual-class-destructor `_, "Yes" `darwin-avoid-spinlock `_, `darwin-dispatch-once-nonstatic `_, "Yes" diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/symmetric-binary-operator.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/symmetric-binary-operator.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/symmetric-binary-operator.cpp @@ -0,0 +1,481 @@ +// 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 +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 +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 +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 +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 +struct H { + int X; + friend bool operator==(const H &, const H &) = 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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +bool C::operator==(const C &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 +struct A { + bool operator==(const T &); +}; + +struct B { + template + bool operator==(const T &); +}; + +template +struct C { + template + bool operator==(const C &); + bool operator!=(const C &); +}; + +} // 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 +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 +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 +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 +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 +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 +using SetVector = ::std::vector; + +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 +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 &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 &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 { + using super = IntegerStateBase; + using base_t = IntegerStateBase::base_t; + + BooleanState() = default; + BooleanState(base_t Assumed) : super(Assumed) {} +}; + +template +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 Set; +}; + +} // namespace llvm_omp_testcase