[clang-tools-extra] a084854 - [clang-tidy] Add readability-operators-representation check

Piotr Zegar via cfe-commits cfe-commits at lists.llvm.org
Fri Mar 31 09:07:27 PDT 2023


Author: Piotr Zegar
Date: 2023-03-31T16:07:16Z
New Revision: a084854266ca60748982228a4c98d036bca5f762

URL: https://github.com/llvm/llvm-project/commit/a084854266ca60748982228a4c98d036bca5f762
DIFF: https://github.com/llvm/llvm-project/commit/a084854266ca60748982228a4c98d036bca5f762.diff

LOG: [clang-tidy] Add readability-operators-representation check

Check helps enforce consistent token representation for binary, unary and
overloaded operators in C++ code. The check supports both traditional and
alternative representations of operators.

Reviewed By: carlosgalvezp

Differential Revision: https://reviews.llvm.org/D144522

Added: 
    clang-tools-extra/clang-tidy/readability/OperatorsRepresentationCheck.cpp
    clang-tools-extra/clang-tidy/readability/OperatorsRepresentationCheck.h
    clang-tools-extra/docs/clang-tidy/checks/readability/operators-representation.rst
    clang-tools-extra/test/clang-tidy/checkers/readability/operators-representation-to-alternative.cpp
    clang-tools-extra/test/clang-tidy/checkers/readability/operators-representation-to-traditional.cpp

Modified: 
    clang-tools-extra/clang-tidy/readability/CMakeLists.txt
    clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
    clang-tools-extra/docs/ReleaseNotes.rst
    clang-tools-extra/docs/clang-tidy/checks/list.rst

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
index 2306641ca9215..421698cd615f9 100644
--- a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
@@ -29,6 +29,7 @@ add_clang_library(clangTidyReadabilityModule
   NamedParameterCheck.cpp
   NamespaceCommentCheck.cpp
   NonConstParameterCheck.cpp
+  OperatorsRepresentationCheck.cpp
   QualifiedAutoCheck.cpp
   ReadabilityTidyModule.cpp
   RedundantAccessSpecifiersCheck.cpp

diff  --git a/clang-tools-extra/clang-tidy/readability/OperatorsRepresentationCheck.cpp b/clang-tools-extra/clang-tidy/readability/OperatorsRepresentationCheck.cpp
new file mode 100644
index 0000000000000..ab45dab71689a
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/readability/OperatorsRepresentationCheck.cpp
@@ -0,0 +1,335 @@
+//===--- OperatorsRepresentationCheck.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 "OperatorsRepresentationCheck.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/STLExtras.h"
+#include <array>
+#include <utility>
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+static StringRef getOperatorSpelling(SourceLocation Loc, ASTContext &Context) {
+  if (Loc.isInvalid())
+    return {};
+
+  SourceManager &SM = Context.getSourceManager();
+
+  Loc = SM.getSpellingLoc(Loc);
+  if (Loc.isInvalid())
+    return {};
+
+  const CharSourceRange TokenRange = CharSourceRange::getTokenRange(Loc);
+  return Lexer::getSourceText(TokenRange, SM, Context.getLangOpts());
+}
+
+namespace {
+
+AST_MATCHER_P2(BinaryOperator, hasInvalidBinaryOperatorRepresentation,
+               BinaryOperatorKind, Kind, llvm::StringRef,
+               ExpectedRepresentation) {
+  if (Node.getOpcode() != Kind || ExpectedRepresentation.empty())
+    return false;
+
+  StringRef Spelling =
+      getOperatorSpelling(Node.getOperatorLoc(), Finder->getASTContext());
+  return !Spelling.empty() && Spelling != ExpectedRepresentation;
+}
+
+AST_MATCHER_P2(UnaryOperator, hasInvalidUnaryOperatorRepresentation,
+               UnaryOperatorKind, Kind, llvm::StringRef,
+               ExpectedRepresentation) {
+  if (Node.getOpcode() != Kind || ExpectedRepresentation.empty())
+    return false;
+
+  StringRef Spelling =
+      getOperatorSpelling(Node.getOperatorLoc(), Finder->getASTContext());
+  return !Spelling.empty() && Spelling != ExpectedRepresentation;
+}
+
+AST_MATCHER_P2(CXXOperatorCallExpr, hasInvalidOverloadedOperatorRepresentation,
+               OverloadedOperatorKind, Kind, llvm::StringRef,
+               ExpectedRepresentation) {
+  if (Node.getOperator() != Kind || ExpectedRepresentation.empty())
+    return false;
+
+  StringRef Spelling =
+      getOperatorSpelling(Node.getOperatorLoc(), Finder->getASTContext());
+  return !Spelling.empty() && Spelling != ExpectedRepresentation;
+}
+
+} // namespace
+
+constexpr std::array<std::pair<llvm::StringRef, llvm::StringRef>, 2U>
+    UnaryRepresentation{{{"!", "not"}, {"~", "compl"}}};
+
+constexpr std::array<std::pair<llvm::StringRef, llvm::StringRef>, 9U>
+    OperatorsRepresentation{{{"&&", "and"},
+                             {"||", "or"},
+                             {"^", "xor"},
+                             {"&", "bitand"},
+                             {"|", "bitor"},
+                             {"&=", "and_eq"},
+                             {"|=", "or_eq"},
+                             {"!=", "not_eq"},
+                             {"^=", "xor_eq"}}};
+
+static llvm::StringRef translate(llvm::StringRef Value) {
+  for (const auto &[Traditional, Alternative] : UnaryRepresentation) {
+    if (Value == Traditional)
+      return Alternative;
+    if (Value == Alternative)
+      return Traditional;
+  }
+
+  for (const auto &[Traditional, Alternative] : OperatorsRepresentation) {
+    if (Value == Traditional)
+      return Alternative;
+    if (Value == Alternative)
+      return Traditional;
+  }
+  return {};
+}
+
+static bool isNotOperatorStr(llvm::StringRef Value) {
+  return translate(Value).empty();
+}
+
+static bool isSeparator(char C) noexcept {
+  constexpr llvm::StringRef Separators(" \t\r\n\0()<>{};,");
+  return llvm::is_contained(Separators, C);
+}
+
+static bool needEscaping(llvm::StringRef Operator) {
+  switch (Operator[0]) {
+  case '&':
+  case '|':
+  case '!':
+  case '^':
+  case '~':
+    return false;
+  default:
+    return true;
+  }
+}
+
+static llvm::StringRef
+getRepresentation(const std::vector<llvm::StringRef> &Config,
+                  llvm::StringRef Traditional, llvm::StringRef Alternative) {
+  if (llvm::is_contained(Config, Traditional))
+    return Traditional;
+  if (llvm::is_contained(Config, Alternative))
+    return Alternative;
+  return {};
+}
+
+template <typename T>
+static bool isAnyOperatorEnabled(const std::vector<llvm::StringRef> &Config,
+                                 T &&Operators) {
+  for (const auto &[traditional, alternative] : Operators) {
+    if (!getRepresentation(Config, traditional, alternative).empty())
+      return true;
+  }
+  return false;
+}
+
+OperatorsRepresentationCheck::OperatorsRepresentationCheck(
+    StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      BinaryOperators(
+          utils::options::parseStringList(Options.get("BinaryOperators", ""))),
+      OverloadedOperators(utils::options::parseStringList(
+          Options.get("OverloadedOperators", ""))) {
+  llvm::erase_if(BinaryOperators, isNotOperatorStr);
+  llvm::erase_if(OverloadedOperators, isNotOperatorStr);
+}
+
+void OperatorsRepresentationCheck::storeOptions(
+    ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "BinaryOperators",
+                utils::options::serializeStringList(BinaryOperators));
+  Options.store(Opts, "OverloadedOperators",
+                utils::options::serializeStringList(OverloadedOperators));
+}
+
+std::optional<TraversalKind>
+OperatorsRepresentationCheck::getCheckTraversalKind() const {
+  return TK_IgnoreUnlessSpelledInSource;
+}
+
+bool OperatorsRepresentationCheck::isLanguageVersionSupported(
+    const LangOptions &LangOpts) const {
+  return LangOpts.CPlusPlus;
+}
+
+void OperatorsRepresentationCheck::registerBinaryOperatorMatcher(
+    MatchFinder *Finder) {
+  if (!isAnyOperatorEnabled(BinaryOperators, OperatorsRepresentation))
+    return;
+
+  Finder->addMatcher(
+      binaryOperator(
+          unless(isExpansionInSystemHeader()),
+          anyOf(hasInvalidBinaryOperatorRepresentation(
+                    BO_LAnd, getRepresentation(BinaryOperators, "&&", "and")),
+                hasInvalidBinaryOperatorRepresentation(
+                    BO_LOr, getRepresentation(BinaryOperators, "||", "or")),
+                hasInvalidBinaryOperatorRepresentation(
+                    BO_NE, getRepresentation(BinaryOperators, "!=", "not_eq")),
+                hasInvalidBinaryOperatorRepresentation(
+                    BO_Xor, getRepresentation(BinaryOperators, "^", "xor")),
+                hasInvalidBinaryOperatorRepresentation(
+                    BO_And, getRepresentation(BinaryOperators, "&", "bitand")),
+                hasInvalidBinaryOperatorRepresentation(
+                    BO_Or, getRepresentation(BinaryOperators, "|", "bitor")),
+                hasInvalidBinaryOperatorRepresentation(
+                    BO_AndAssign,
+                    getRepresentation(BinaryOperators, "&=", "and_eq")),
+                hasInvalidBinaryOperatorRepresentation(
+                    BO_OrAssign,
+                    getRepresentation(BinaryOperators, "|=", "or_eq")),
+                hasInvalidBinaryOperatorRepresentation(
+                    BO_XorAssign,
+                    getRepresentation(BinaryOperators, "^=", "xor_eq"))))
+          .bind("binary_op"),
+      this);
+}
+
+void OperatorsRepresentationCheck::registerUnaryOperatorMatcher(
+    MatchFinder *Finder) {
+  if (!isAnyOperatorEnabled(BinaryOperators, UnaryRepresentation))
+    return;
+
+  Finder->addMatcher(
+      unaryOperator(
+          unless(isExpansionInSystemHeader()),
+          anyOf(hasInvalidUnaryOperatorRepresentation(
+                    UO_LNot, getRepresentation(BinaryOperators, "!", "not")),
+                hasInvalidUnaryOperatorRepresentation(
+                    UO_Not, getRepresentation(BinaryOperators, "~", "compl"))))
+          .bind("unary_op"),
+      this);
+}
+
+void OperatorsRepresentationCheck::registerOverloadedOperatorMatcher(
+    MatchFinder *Finder) {
+  if (!isAnyOperatorEnabled(OverloadedOperators, OperatorsRepresentation) &&
+      !isAnyOperatorEnabled(OverloadedOperators, UnaryRepresentation))
+    return;
+
+  Finder->addMatcher(
+      cxxOperatorCallExpr(
+          unless(isExpansionInSystemHeader()),
+          anyOf(
+              hasInvalidOverloadedOperatorRepresentation(
+                  OO_AmpAmp,
+                  getRepresentation(OverloadedOperators, "&&", "and")),
+              hasInvalidOverloadedOperatorRepresentation(
+                  OO_PipePipe,
+                  getRepresentation(OverloadedOperators, "||", "or")),
+              hasInvalidOverloadedOperatorRepresentation(
+                  OO_Exclaim,
+                  getRepresentation(OverloadedOperators, "!", "not")),
+              hasInvalidOverloadedOperatorRepresentation(
+                  OO_ExclaimEqual,
+                  getRepresentation(OverloadedOperators, "!=", "not_eq")),
+              hasInvalidOverloadedOperatorRepresentation(
+                  OO_Caret, getRepresentation(OverloadedOperators, "^", "xor")),
+              hasInvalidOverloadedOperatorRepresentation(
+                  OO_Amp,
+                  getRepresentation(OverloadedOperators, "&", "bitand")),
+              hasInvalidOverloadedOperatorRepresentation(
+                  OO_Pipe,
+                  getRepresentation(OverloadedOperators, "|", "bitor")),
+              hasInvalidOverloadedOperatorRepresentation(
+                  OO_AmpEqual,
+                  getRepresentation(OverloadedOperators, "&=", "and_eq")),
+              hasInvalidOverloadedOperatorRepresentation(
+                  OO_PipeEqual,
+                  getRepresentation(OverloadedOperators, "|=", "or_eq")),
+              hasInvalidOverloadedOperatorRepresentation(
+                  OO_CaretEqual,
+                  getRepresentation(OverloadedOperators, "^=", "xor_eq")),
+              hasInvalidOverloadedOperatorRepresentation(
+                  OO_Tilde,
+                  getRepresentation(OverloadedOperators, "~", "compl"))))
+          .bind("overloaded_op"),
+      this);
+}
+
+void OperatorsRepresentationCheck::registerMatchers(MatchFinder *Finder) {
+  registerBinaryOperatorMatcher(Finder);
+  registerUnaryOperatorMatcher(Finder);
+  registerOverloadedOperatorMatcher(Finder);
+}
+
+void OperatorsRepresentationCheck::check(
+    const MatchFinder::MatchResult &Result) {
+
+  SourceLocation Loc;
+
+  if (const auto *Op = Result.Nodes.getNodeAs<BinaryOperator>("binary_op"))
+    Loc = Op->getOperatorLoc();
+  else if (const auto *Op = Result.Nodes.getNodeAs<UnaryOperator>("unary_op"))
+    Loc = Op->getOperatorLoc();
+  else if (const auto *Op =
+               Result.Nodes.getNodeAs<CXXOperatorCallExpr>("overloaded_op"))
+    Loc = Op->getOperatorLoc();
+
+  if (Loc.isInvalid())
+    return;
+
+  Loc = Result.SourceManager->getSpellingLoc(Loc);
+  if (Loc.isInvalid() || Loc.isMacroID())
+    return;
+
+  const CharSourceRange TokenRange = CharSourceRange::getTokenRange(Loc);
+  if (TokenRange.isInvalid())
+    return;
+
+  StringRef Spelling = Lexer::getSourceText(TokenRange, *Result.SourceManager,
+                                            Result.Context->getLangOpts());
+  StringRef TranslatedSpelling = translate(Spelling);
+
+  if (TranslatedSpelling.empty())
+    return;
+
+  std::string FixSpelling = TranslatedSpelling.str();
+
+  StringRef SourceRepresentation = "an alternative";
+  StringRef TargetRepresentation = "a traditional";
+  if (needEscaping(TranslatedSpelling)) {
+    SourceRepresentation = "a traditional";
+    TargetRepresentation = "an alternative";
+
+    StringRef SpellingEx = Lexer::getSourceText(
+        CharSourceRange::getCharRange(
+            TokenRange.getBegin().getLocWithOffset(-1),
+            TokenRange.getBegin().getLocWithOffset(Spelling.size() + 1U)),
+        *Result.SourceManager, Result.Context->getLangOpts());
+    if (SpellingEx.empty() || !isSeparator(SpellingEx.front()))
+      FixSpelling.insert(FixSpelling.begin(), ' ');
+    if (SpellingEx.empty() || !isSeparator(SpellingEx.back()))
+      FixSpelling.push_back(' ');
+  }
+
+  diag(
+      Loc,
+      "'%0' is %1 token spelling, consider using %2 token '%3' for consistency")
+      << Spelling << SourceRepresentation << TargetRepresentation
+      << TranslatedSpelling
+      << FixItHint::CreateReplacement(TokenRange, FixSpelling);
+}
+
+} // namespace clang::tidy::readability

diff  --git a/clang-tools-extra/clang-tidy/readability/OperatorsRepresentationCheck.h b/clang-tools-extra/clang-tidy/readability/OperatorsRepresentationCheck.h
new file mode 100644
index 0000000000000..d315f3912a914
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/readability/OperatorsRepresentationCheck.h
@@ -0,0 +1,42 @@
+//===--- OperatorsRepresentationCheck.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_OPERATORSREPRESENTATIONCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_OPERATORSREPRESENTATIONCHECK_H
+
+#include "../ClangTidyCheck.h"
+#include <vector>
+
+namespace clang::tidy::readability {
+
+/// Enforces consistent token representation for invoked binary, unary
+/// and overloaded operators in C++ code.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability/operators-representation.html
+class OperatorsRepresentationCheck : public ClangTidyCheck {
+public:
+  OperatorsRepresentationCheck(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;
+  std::optional<TraversalKind> getCheckTraversalKind() const override;
+
+private:
+  void registerBinaryOperatorMatcher(ast_matchers::MatchFinder *Finder);
+  void registerUnaryOperatorMatcher(ast_matchers::MatchFinder *Finder);
+  void registerOverloadedOperatorMatcher(ast_matchers::MatchFinder *Finder);
+
+  std::vector<llvm::StringRef> BinaryOperators;
+  std::vector<llvm::StringRef> OverloadedOperators;
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_OPERATORSREPRESENTATIONCHECK_H

diff  --git a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
index 7fd9191844897..f95fa636f1577 100644
--- a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
@@ -33,6 +33,7 @@
 #include "MisplacedArrayIndexCheck.h"
 #include "NamedParameterCheck.h"
 #include "NonConstParameterCheck.h"
+#include "OperatorsRepresentationCheck.h"
 #include "QualifiedAutoCheck.h"
 #include "RedundantAccessSpecifiersCheck.h"
 #include "RedundantControlFlowCheck.h"
@@ -103,6 +104,8 @@ class ReadabilityModule : public ClangTidyModule {
         "readability-misleading-indentation");
     CheckFactories.registerCheck<MisplacedArrayIndexCheck>(
         "readability-misplaced-array-index");
+    CheckFactories.registerCheck<OperatorsRepresentationCheck>(
+        "readability-operators-representation");
     CheckFactories.registerCheck<QualifiedAutoCheck>(
         "readability-qualified-auto");
     CheckFactories.registerCheck<RedundantAccessSpecifiersCheck>(

diff  --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 413f8925aec2c..156e9d73c7be0 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -140,6 +140,12 @@ New checks
   directives by analyzing ``#if`` conditions, such as ``#if 0`` and
   ``#if 1``, etc.
 
+- New :doc:`readability-operators-representation
+  <clang-tidy/checks/readability/operators-representation>` check.
+
+  Enforces consistent token representation for invoked binary, unary and
+  overloaded operators in C++ code.
+
 New check aliases
 ^^^^^^^^^^^^^^^^^
 

diff  --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 095f32f38da77..44530a09b2479 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -354,6 +354,7 @@ Clang-Tidy Checks
    `readability-misplaced-array-index <readability/misplaced-array-index.html>`_, "Yes"
    `readability-named-parameter <readability/named-parameter.html>`_, "Yes"
    `readability-non-const-parameter <readability/non-const-parameter.html>`_, "Yes"
+   `readability-operators-representation <readability/operators-representation.html>`_, "Yes"
    `readability-qualified-auto <readability/qualified-auto.html>`_, "Yes"
    `readability-redundant-access-specifiers <readability/redundant-access-specifiers.html>`_, "Yes"
    `readability-redundant-control-flow <readability/redundant-control-flow.html>`_, "Yes"

diff  --git a/clang-tools-extra/docs/clang-tidy/checks/readability/operators-representation.rst b/clang-tools-extra/docs/clang-tidy/checks/readability/operators-representation.rst
new file mode 100644
index 0000000000000..b84f7a1c40bd4
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/readability/operators-representation.rst
@@ -0,0 +1,86 @@
+.. title:: clang-tidy - readability-operators-representation
+
+readability-operators-representation
+====================================
+
+Enforces consistent token representation for invoked binary, unary and
+overloaded operators in C++ code. The check supports both traditional and
+alternative representations of operators, such as ``&&`` and ``and``, ``||``
+and ``or``, and so on.
+
+In the realm of C++ programming, developers have the option to choose between
+two distinct representations for operators: traditional token representation
+and alternative token representation. Traditional tokens utilize symbols,
+such as ``&&``, ``||``, and ``!``, while alternative tokens employ more
+descriptive words like ``and``, ``or``, and ``not``.
+
+In the following mapping table, a comprehensive list of traditional and
+alternative tokens, along with their corresponding representations,
+is presented:
+
+.. table:: Token Representation Mapping Table
+    :widths: auto
+
+    =========== ===========
+    Traditional Alternative
+    =========== ===========
+    ``&&``      ``and``
+    ``&=``      ``and_eq``
+    ``&``       ``bitand``
+    ``|``       ``bitor``
+    ``~``       ``compl``
+    ``!``       ``not``
+    ``!=``      ``not_eq``
+    ``||``      ``or``
+    ``|=``      ``or_eq``
+    ``^``       ``xor``
+    ``^=``      ``xor_eq``
+    =========== ===========
+
+Example
+-------
+
+.. code-block:: c++
+
+    // Traditional Token Representation:
+
+    if (!a||!b)
+    {
+        // do something
+    }
+
+    // Alternative Token Representation:
+
+    if (not a or not b)
+    {
+        // do something
+    }
+
+Options
+-------
+
+Due to the distinct benefits and drawbacks of each representation, the default
+configuration doesn't enforce either. Explicit configuration is needed.
+
+To configure check to enforce Traditional Token Representation for all
+operators set options to `&&;&=;&;|;~;!;!=;||;|=;^;^=`.
+
+To configure check to enforce Alternative Token Representation for all
+operators set options to
+`and;and_eq;bitand;bitor;compl;not;not_eq;or;or_eq;xor;xor_eq`.
+
+Developers do not need to enforce all operators, and can mix the representations
+as desired by specifying a semicolon-separated list of both traditional and
+alternative tokens in the configuration, such as `and;||;not`.
+
+.. option:: BinaryOperators
+
+    This option allows you to specify a semicolon-separated list of binary
+    operators for which you want to enforce specific token representation.
+    The default value is empty string.
+
+.. option:: OverloadedOperators
+
+    This option allows you to specify a semicolon-separated list of overloaded
+    operators for which you want to enforce specific token representation.
+    The default value is empty string.

diff  --git a/clang-tools-extra/test/clang-tidy/checkers/readability/operators-representation-to-alternative.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/operators-representation-to-alternative.cpp
new file mode 100644
index 0000000000000..ebaa30dbe29c2
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/operators-representation-to-alternative.cpp
@@ -0,0 +1,177 @@
+// RUN: %check_clang_tidy %s readability-operators-representation %t -- -config="{CheckOptions: [\
+// RUN: {key: readability-operators-representation.BinaryOperators, value: 'and;and_eq;bitand;bitor;compl;not;not_eq;or;or_eq;xor;xor_eq'}, \
+// RUN: {key: readability-operators-representation.OverloadedOperators, value: 'and;and_eq;bitand;bitor;compl;not;not_eq;or;or_eq;xor;xor_eq'}]}" --
+
+void testAllTokensToAlternative(int a, int b) {
+  int value = 0;
+
+  value = a||b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: '||' is a traditional token spelling, consider using an alternative token 'or' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}value = a or b;{{$}}
+
+  value = a&&b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: '&&' is a traditional token spelling, consider using an alternative token 'and' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}value = a and b;{{$}}
+
+  value = a | b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: '|' is a traditional token spelling, consider using an alternative token 'bitor' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}value = a bitor b;{{$}}
+
+  value = a & b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: '&' is a traditional token spelling, consider using an alternative token 'bitand' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}value = a bitand b;{{$}}
+
+  value = !a;
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: '!' is a traditional token spelling, consider using an alternative token 'not' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}value = not a;{{$}}
+
+  value = a^b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: '^' is a traditional token spelling, consider using an alternative token 'xor' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}value = a xor b;{{$}}
+
+  value = ~b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: '~' is a traditional token spelling, consider using an alternative token 'compl' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}value = compl b;{{$}}
+
+  value &= b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: '&=' is a traditional token spelling, consider using an alternative token 'and_eq' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}value and_eq b;{{$}}
+
+  value |= b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: '|=' is a traditional token spelling, consider using an alternative token 'or_eq' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}value or_eq b;{{$}}
+
+  value = a != b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: '!=' is a traditional token spelling, consider using an alternative token 'not_eq' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}value = a not_eq b;{{$}}
+
+  value ^= a;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: '^=' is a traditional token spelling, consider using an alternative token 'xor_eq' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}value xor_eq a;{{$}}
+}
+
+struct Class {
+  bool operator!() const;
+  Class operator~() const;
+  bool operator&&(const Class&) const;
+  Class operator&(const Class&) const;
+  bool operator||(const Class&) const;
+  Class operator|(const Class&) const;
+  Class operator^(const Class&) const;
+  Class& operator&=(const Class&) const;
+  Class& operator|=(const Class&) const;
+  Class& operator^=(const Class&) const;
+  bool operator!=(const Class&) const;
+};
+
+void testAllTokensToAlternative(Class a, Class b) {
+  int value = 0;
+  Class clval;
+
+  value = a||b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: '||' is a traditional token spelling, consider using an alternative token 'or' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}value = a or b;{{$}}
+
+  value = a&&b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: '&&' is a traditional token spelling, consider using an alternative token 'and' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}value = a and b;{{$}}
+
+  clval = a | b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: '|' is a traditional token spelling, consider using an alternative token 'bitor' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}clval = a bitor b;{{$}}
+
+  clval = a & b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: '&' is a traditional token spelling, consider using an alternative token 'bitand' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}clval = a bitand b;{{$}}
+
+  value = !a;
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: '!' is a traditional token spelling, consider using an alternative token 'not' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}value = not a;{{$}}
+
+  clval = a^b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: '^' is a traditional token spelling, consider using an alternative token 'xor' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}clval = a xor b;{{$}}
+
+  clval = ~b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: '~' is a traditional token spelling, consider using an alternative token 'compl' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}clval = compl b;{{$}}
+
+  clval &= b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: '&=' is a traditional token spelling, consider using an alternative token 'and_eq' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}clval and_eq b;{{$}}
+
+  clval |= b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: '|=' is a traditional token spelling, consider using an alternative token 'or_eq' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}clval or_eq b;{{$}}
+
+  value = a != b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: '!=' is a traditional token spelling, consider using an alternative token 'not_eq' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}value = a not_eq b;{{$}}
+
+  clval ^= a;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: '^=' is a traditional token spelling, consider using an alternative token 'xor_eq' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}clval xor_eq a;{{$}}
+}
+
+struct ClassO {};
+
+ClassO& operator&=(ClassO&, const ClassO&);
+ClassO& operator|=(ClassO&, const ClassO&);
+ClassO& operator^=(ClassO&, const ClassO&);
+bool operator!=(const ClassO&, const ClassO&);
+bool operator&&(const ClassO&, const ClassO&);
+bool operator||(const ClassO&, const ClassO&);
+bool operator!(const ClassO&);
+ClassO operator&(const ClassO&, const ClassO&);
+ClassO operator|(const ClassO&, const ClassO&);
+ClassO operator^(const ClassO&, const ClassO&);
+ClassO operator~(const ClassO&);
+
+void testAllTokensToAlternative(ClassO a, ClassO b) {
+  int value = 0;
+  ClassO clval;
+
+  value = a||b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: '||' is a traditional token spelling, consider using an alternative token 'or' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}value = a or b;{{$}}
+
+  value = a&&b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: '&&' is a traditional token spelling, consider using an alternative token 'and' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}value = a and b;{{$}}
+
+  clval = a | b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: '|' is a traditional token spelling, consider using an alternative token 'bitor' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}clval = a bitor b;{{$}}
+
+  clval = a & b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: '&' is a traditional token spelling, consider using an alternative token 'bitand' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}clval = a bitand b;{{$}}
+
+  value = !a;
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: '!' is a traditional token spelling, consider using an alternative token 'not' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}value = not a;{{$}}
+
+  clval = a^b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: '^' is a traditional token spelling, consider using an alternative token 'xor' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}clval = a xor b;{{$}}
+
+  clval = ~b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: '~' is a traditional token spelling, consider using an alternative token 'compl' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}clval = compl b;{{$}}
+
+  clval &= b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: '&=' is a traditional token spelling, consider using an alternative token 'and_eq' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}clval and_eq b;{{$}}
+
+  clval |= b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: '|=' is a traditional token spelling, consider using an alternative token 'or_eq' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}clval or_eq b;{{$}}
+
+  value = a != b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: '!=' is a traditional token spelling, consider using an alternative token 'not_eq' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}value = a not_eq b;{{$}}
+
+  clval ^= a;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: '^=' is a traditional token spelling, consider using an alternative token 'xor_eq' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}clval xor_eq a;{{$}}
+}

diff  --git a/clang-tools-extra/test/clang-tidy/checkers/readability/operators-representation-to-traditional.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/operators-representation-to-traditional.cpp
new file mode 100644
index 0000000000000..e1e47dbf2268f
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/operators-representation-to-traditional.cpp
@@ -0,0 +1,177 @@
+// RUN: %check_clang_tidy %s readability-operators-representation %t -- -config="{CheckOptions: [\
+// RUN: {key: readability-operators-representation.BinaryOperators, value: '&&;&=;&;|;~;!;!=;||;|=;^;^='}, \
+// RUN: {key: readability-operators-representation.OverloadedOperators, value: '&&;&=;&;|;~;!;!=;||;|=;^;^='}]}" --
+
+void testAllTokensToAlternative(int a, int b) {
+  int value = 0;
+
+  value = a or b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: 'or' is an alternative token spelling, consider using a traditional token '||' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}value = a || b;{{$}}
+
+  value = a and b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: 'and' is an alternative token spelling, consider using a traditional token '&&' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}value = a && b;{{$}}
+
+  value = a bitor b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: 'bitor' is an alternative token spelling, consider using a traditional token '|' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}value = a | b;{{$}}
+
+  value = a bitand b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: 'bitand' is an alternative token spelling, consider using a traditional token '&' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}value = a & b;{{$}}
+
+  value = not a;
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: 'not' is an alternative token spelling, consider using a traditional token '!' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}value = ! a;{{$}}
+
+  value = a xor b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: 'xor' is an alternative token spelling, consider using a traditional token '^' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}value = a ^ b;{{$}}
+
+  value = compl b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: 'compl' is an alternative token spelling, consider using a traditional token '~' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}value = ~ b;{{$}}
+
+  value and_eq b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: 'and_eq' is an alternative token spelling, consider using a traditional token '&=' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}value &= b;{{$}}
+
+  value or_eq b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: 'or_eq' is an alternative token spelling, consider using a traditional token '|=' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}value |= b;{{$}}
+
+  value = a not_eq b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: 'not_eq' is an alternative token spelling, consider using a traditional token '!=' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}value = a != b;{{$}}
+
+  value xor_eq a;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: 'xor_eq' is an alternative token spelling, consider using a traditional token '^=' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}value ^= a;{{$}}
+}
+
+struct Class {
+  bool operator!() const;
+  Class operator~() const;
+  bool operator&&(const Class&) const;
+  Class operator&(const Class&) const;
+  bool operator||(const Class&) const;
+  Class operator|(const Class&) const;
+  Class operator^(const Class&) const;
+  Class& operator&=(const Class&) const;
+  Class& operator|=(const Class&) const;
+  Class& operator^=(const Class&) const;
+  bool operator!=(const Class&) const;
+};
+
+void testAllTokensToAlternative(Class a, Class b) {
+  int value = 0;
+  Class clval;
+
+  value = a or b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: 'or' is an alternative token spelling, consider using a traditional token '||' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}value = a || b;{{$}}
+
+  value = a and b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: 'and' is an alternative token spelling, consider using a traditional token '&&' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}value = a && b;{{$}}
+
+  clval = a bitor b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: 'bitor' is an alternative token spelling, consider using a traditional token '|' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}clval = a | b;{{$}}
+
+  clval = a bitand b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: 'bitand' is an alternative token spelling, consider using a traditional token '&' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}clval = a & b;{{$}}
+
+  value = not a;
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: 'not' is an alternative token spelling, consider using a traditional token '!' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}value = ! a;{{$}}
+
+  clval = a xor b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: 'xor' is an alternative token spelling, consider using a traditional token '^' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}clval = a ^ b;{{$}}
+
+  clval = compl b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: 'compl' is an alternative token spelling, consider using a traditional token '~' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}clval = ~ b;{{$}}
+
+  clval and_eq b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: 'and_eq' is an alternative token spelling, consider using a traditional token '&=' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}clval &= b;{{$}}
+
+  clval or_eq b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: 'or_eq' is an alternative token spelling, consider using a traditional token '|=' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}clval |= b;{{$}}
+
+  value = a not_eq b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: 'not_eq' is an alternative token spelling, consider using a traditional token '!=' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}value = a != b;{{$}}
+
+  clval xor_eq a;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: 'xor_eq' is an alternative token spelling, consider using a traditional token '^=' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}clval ^= a;{{$}}
+}
+
+struct ClassO {};
+
+ClassO& operator&=(ClassO&, const ClassO&);
+ClassO& operator|=(ClassO&, const ClassO&);
+ClassO& operator^=(ClassO&, const ClassO&);
+bool operator!=(const ClassO&, const ClassO&);
+bool operator&&(const ClassO&, const ClassO&);
+bool operator||(const ClassO&, const ClassO&);
+bool operator!(const ClassO&);
+ClassO operator&(const ClassO&, const ClassO&);
+ClassO operator|(const ClassO&, const ClassO&);
+ClassO operator^(const ClassO&, const ClassO&);
+ClassO operator~(const ClassO&);
+
+void testAllTokensToAlternative(ClassO a, ClassO b) {
+  int value = 0;
+  ClassO clval;
+
+  value = a or b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: 'or' is an alternative token spelling, consider using a traditional token '||' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}value = a || b;{{$}}
+
+  value = a and b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: 'and' is an alternative token spelling, consider using a traditional token '&&' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}value = a && b;{{$}}
+
+  clval = a bitor b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: 'bitor' is an alternative token spelling, consider using a traditional token '|' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}clval = a | b;{{$}}
+
+  clval = a bitand b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: 'bitand' is an alternative token spelling, consider using a traditional token '&' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}clval = a & b;{{$}}
+
+  value = not a;
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: 'not' is an alternative token spelling, consider using a traditional token '!' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}value = ! a;{{$}}
+
+  clval = a xor b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: 'xor' is an alternative token spelling, consider using a traditional token '^' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}clval = a ^ b;{{$}}
+
+  clval = compl b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: 'compl' is an alternative token spelling, consider using a traditional token '~' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}clval = ~ b;{{$}}
+
+  clval and_eq b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: 'and_eq' is an alternative token spelling, consider using a traditional token '&=' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}clval &= b;{{$}}
+
+  clval or_eq b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: 'or_eq' is an alternative token spelling, consider using a traditional token '|=' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}clval |= b;{{$}}
+
+  value = a not_eq b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: 'not_eq' is an alternative token spelling, consider using a traditional token '!=' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}value = a != b;{{$}}
+
+  clval xor_eq a;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: 'xor_eq' is an alternative token spelling, consider using a traditional token '^=' for consistency [readability-operators-representation]
+  // CHECK-FIXES: {{^  }}clval ^= a;{{$}}
+}


        


More information about the cfe-commits mailing list