diff --git a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt --- a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt @@ -28,6 +28,7 @@ NamedParameterCheck.cpp NamespaceCommentCheck.cpp NonConstParameterCheck.cpp + OperatorsRepresentationCheck.cpp QualifiedAutoCheck.cpp ReadabilityTidyModule.cpp RedundantAccessSpecifiersCheck.cpp diff --git a/clang-tools-extra/clang-tidy/readability/OperatorsRepresentationCheck.h b/clang-tools-extra/clang-tidy/readability/OperatorsRepresentationCheck.h new file mode 100644 --- /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 + +namespace clang::tidy::readability { + +/// Check helps enforce 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 getCheckTraversalKind() const override; + +private: + void registerBinaryOperatorMatcher(ast_matchers::MatchFinder *Finder); + void registerUnaryOperatorMatcher(ast_matchers::MatchFinder *Finder); + void registerOverloadedOperatorMatcher(ast_matchers::MatchFinder *Finder); + + std::vector BinaryOperators; + std::vector OverloadedOperators; +}; + +} // namespace clang::tidy::readability + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_OPERATORSREPRESENTATIONCHECK_H diff --git a/clang-tools-extra/clang-tidy/readability/OperatorsRepresentationCheck.cpp b/clang-tools-extra/clang-tidy/readability/OperatorsRepresentationCheck.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/readability/OperatorsRepresentationCheck.cpp @@ -0,0 +1,332 @@ +//===--- 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 +#include + +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, 2U> + UnaryRepresentation{{{"!", "not"}, {"~", "compl"}}}; + +constexpr std::array, 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 &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 +static bool isAnyOperatorEnabled(const std::vector &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 +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("binary_op")) + Loc = Op->getOperatorLoc(); + else if (const auto *Op = Result.Nodes.getNodeAs("unary_op")) + Loc = Op->getOperatorLoc(); + else if (const auto *Op = Result.Nodes.getNodeAs("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/ReadabilityTidyModule.cpp b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp --- a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp @@ -32,6 +32,7 @@ #include "MisplacedArrayIndexCheck.h" #include "NamedParameterCheck.h" #include "NonConstParameterCheck.h" +#include "OperatorsRepresentationCheck.h" #include "QualifiedAutoCheck.h" #include "RedundantAccessSpecifiersCheck.h" #include "RedundantControlFlowCheck.h" @@ -100,6 +101,8 @@ "readability-misleading-indentation"); CheckFactories.registerCheck( "readability-misplaced-array-index"); + CheckFactories.registerCheck( + "readability-operators-representation"); CheckFactories.registerCheck( "readability-qualified-auto"); CheckFactories.registerCheck( 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 @@ -133,6 +133,12 @@ Checks that all implicit and explicit inline functions in header files are tagged with the ``LIBC_INLINE`` macro. +- New :doc:`readability-operators-representation + ` check. + + Check helps enforce 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 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -353,6 +353,7 @@ `readability-misplaced-array-index `_, "Yes" `readability-named-parameter `_, "Yes" `readability-non-const-parameter `_, "Yes" + `readability-operators-representation `_, "Yes" `readability-qualified-auto `_, "Yes" `readability-redundant-access-specifiers `_, "Yes" `readability-redundant-control-flow `_, "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 --- /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 +==================================== + +Check helps enforce 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 --- /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 --- /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;{{$}} +}