cjdb updated this revision to Diff 364307.
cjdb marked 5 inline comments as done.
cjdb edited the summary of this revision.
cjdb added a comment.
- renames check to 'readability-alternative-tokens'
- adds section in documentation about configurability
- adds support for C source
- corrects whitespace handling in fixits
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D107294/new/
https://reviews.llvm.org/D107294
Files:
clang-tools-extra/clang-tidy/readability/AlternativeTokensCheck.cpp
clang-tools-extra/clang-tidy/readability/AlternativeTokensCheck.h
clang-tools-extra/clang-tidy/readability/CMakeLists.txt
clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
clang-tools-extra/docs/clang-tidy/checks/readability-alternative-tokens.rst
clang-tools-extra/test/clang-tidy/checkers/readability-alternative-tokens-no-warn.c
clang-tools-extra/test/clang-tidy/checkers/readability-alternative-tokens.c
clang-tools-extra/test/clang-tidy/checkers/readability-alternative-tokens.cpp
llvm/utils/gn/secondary/clang-tools-extra/clang-tidy/readability/BUILD.gn
Index: llvm/utils/gn/secondary/clang-tools-extra/clang-tidy/readability/BUILD.gn
===================================================================
--- llvm/utils/gn/secondary/clang-tools-extra/clang-tidy/readability/BUILD.gn
+++ llvm/utils/gn/secondary/clang-tools-extra/clang-tidy/readability/BUILD.gn
@@ -12,6 +12,7 @@
"//llvm/lib/Support",
]
sources = [
+ "AlternativeTokensCheck.cpp",
"AvoidConstParamsInDecls.cpp",
"BracesAroundStatementsCheck.cpp",
"ConstReturnTypeCheck.cpp",
Index: clang-tools-extra/test/clang-tidy/checkers/readability-alternative-tokens.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/readability-alternative-tokens.cpp
@@ -0,0 +1,244 @@
+// RUN: %check_clang_tidy %s readability-alternative-tokens %t
+
+// A note to the reader: the whitespace in this file is important: `true&&false`
+// is lexically three tokens, but `trueandfalse` is lexed as a single
+// identifier. This test needs to make sure that the fixit applies whitespace in
+// its change.
+
+// clang-format off
+
+void logical_and()
+{
+ (void)(1&&0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use 'and' for logical conjunctions
+ // CHECK-FIXES: 1 and 0
+
+ (void)(1&& 0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use 'and' for logical conjunctions
+ // CHECK-FIXES: 1 and 0
+
+ (void)(1 &&0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use 'and' for logical conjunctions
+ // CHECK-FIXES: 1 and 0
+
+ (void)(1 && 0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use 'and' for logical conjunctions
+ // CHECK-FIXES: 1 and 0
+}
+
+void bitwise_and()
+{
+ (void)(0&1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use 'bitand' for bitwise conjunctions
+ // CHECK-FIXES: 0 bitand 1
+
+ (void)(0 &1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use 'bitand' for bitwise conjunctions
+ // CHECK-FIXES: 0 bitand 1
+
+ (void)(0& 1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use 'bitand' for bitwise conjunctions
+ // CHECK-FIXES: 0 bitand 1
+
+ (void)(0 & 1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use 'bitand' for bitwise conjunctions
+ // CHECK-FIXES: 0 bitand 1
+}
+
+void bitwise_or() {
+ (void)(0|1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use 'bitor' for bitwise disjunctions
+ // CHECK-FIXES: 0 bitor 1
+
+ (void)(0| 1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use 'bitor' for bitwise disjunctions
+ // CHECK-FIXES: 0 bitor 1
+
+ (void)(0 |1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use 'bitor' for bitwise disjunctions
+ // CHECK-FIXES: 0 bitor 1
+
+ (void)(0 | 1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use 'bitor' for bitwise disjunctions
+ // CHECK-FIXES: 0 bitor 1
+}
+
+void bitwise_not() {
+ (void)(~0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use 'compl' for bitwise negation
+ // CHECK-FIXES: compl 0
+
+ (void)(~ 0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use 'compl' for bitwise negation
+ // CHECK-FIXES: compl 0
+}
+
+void logical_not() {
+ (void)(!0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use 'not' for logical negation
+ // CHECK-FIXES: not 0
+
+ (void)(! 0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use 'not' for logical negation
+ // CHECK-FIXES: not 0
+}
+
+void logical_or() {
+ (void)(1||0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use 'or' for logical disjunctions
+ // CHECK-FIXES: 1 or 0
+
+ (void)(1|| 0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use 'or' for logical disjunctions
+ // CHECK-FIXES: 1 or 0
+
+ (void)(1 ||0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use 'or' for logical disjunctions
+ // CHECK-FIXES: 1 or 0
+
+ (void)(1 || 0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use 'or' for logical disjunctions
+ // CHECK-FIXES: 1 or 0
+}
+
+void bitwise_xor() {
+ (void)(0^1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use 'xor' for exclusive or
+ // CHECK-FIXES: 0 xor 1
+
+ (void)(0^ 1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use 'xor' for exclusive or
+ // CHECK-FIXES: 0 xor 1
+
+ (void)(0 ^1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use 'xor' for exclusive or
+ // CHECK-FIXES: 0 xor 1
+
+ (void)(0 ^ 1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use 'xor' for exclusive or
+ // CHECK-FIXES: 0 xor 1
+}
+
+struct S {
+ S operator and(S) const;
+ S operator bitand(S) const;
+ S operator bitor(S) const;
+ S operator compl() const;
+ S operator not() const;
+ S operator or(S) const;
+ S operator xor(S) const;
+};
+
+void overloaded_and() {
+ S x;
+ S y;
+
+ (void)(x&&y);
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use 'and' for logical conjunctions
+ // CHECK-FIXES: x and y
+
+ (void)(x&& y);
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use 'and' for logical conjunctions
+ // CHECK-FIXES: x and y
+
+ (void)(x &&y);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use 'and' for logical conjunctions
+ // CHECK-FIXES: x and y
+
+ (void)(x && y);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use 'and' for logical conjunctions
+ // CHECK-FIXES: x and y
+
+ (void)(x&y);
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use 'bitand' for bitwise conjunctions
+ // CHECK-FIXES: x bitand y
+}
+
+void overloaded_bitand() {
+ S x;
+ S y;
+
+ (void)(x|y);
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use 'bitor' for bitwise disjunctions
+ // CHECK-FIXES: x bitor y
+
+ (void)(x| y);
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use 'bitor' for bitwise disjunctions
+ // CHECK-FIXES: x bitor y
+
+ (void)(x |y);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use 'bitor' for bitwise disjunctions
+ // CHECK-FIXES: x bitor y
+
+ (void)(x | y);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use 'bitor' for bitwise disjunctions
+ // CHECK-FIXES: x bitor y
+}
+
+void overloaded_compl() {
+ S x;
+ S y;
+
+ (void)(~x);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use 'compl' for bitwise negation
+ // CHECK-FIXES: compl x
+
+ (void)(~ x);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use 'compl' for bitwise negation
+ // CHECK-FIXES: compl x
+}
+
+void overloaded_not() {
+ S x;
+ S y;
+
+ (void)(!x);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use 'not' for logical negation
+ // CHECK-FIXES: not x
+
+ (void)(! x);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use 'not' for logical negation
+ // CHECK-FIXES: not x
+}
+
+void overloaded_or() {
+ S x;
+ S y;
+
+ (void)(x||y);
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use 'or' for logical disjunctions
+ // CHECK-FIXES: x or y
+
+ (void)(x|| y);
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use 'or' for logical disjunctions
+ // CHECK-FIXES: x or y
+
+ (void)(x ||y);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use 'or' for logical disjunctions
+ // CHECK-FIXES: x or y
+
+ (void)(x || y);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use 'or' for logical disjunctions
+ // CHECK-FIXES: x or y
+}
+
+void overloaded_xor() {
+ S x;
+ S y;
+
+ (void)(x^y);
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use 'xor' for exclusive or
+ // CHECK-FIXES: x xor y
+
+ (void)(x^ y);
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use 'xor' for exclusive or
+ // CHECK-FIXES: x xor y
+
+ (void)(x ^y);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use 'xor' for exclusive or
+ // CHECK-FIXES: x xor y
+
+ (void)(x ^ y);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use 'xor' for exclusive or
+ // CHECK-FIXES: x xor y
+}
Index: clang-tools-extra/test/clang-tidy/checkers/readability-alternative-tokens.c
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/readability-alternative-tokens.c
@@ -0,0 +1,122 @@
+// RUN: %check_clang_tidy %s readability-alternative-tokens %t
+
+// A note to the reader: the whitespace in this file is important: `1&&0`
+// is lexically three tokens, but `1and0` is lexed as a single
+// identifier. This test needs to make sure that the fixit applies whitespace in
+// its change.
+
+// CHECK-FIXES: #include <iso646.h>
+
+// clang-format off
+
+void logical_and()
+{
+ (void)(1&&0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use 'and' for logical conjunctions
+ // CHECK-FIXES: 1 and 0
+
+ (void)(1&& 0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use 'and' for logical conjunctions
+ // CHECK-FIXES: 1 and 0
+
+ (void)(1 &&0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use 'and' for logical conjunctions
+ // CHECK-FIXES: 1 and 0
+
+ (void)(1 && 0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use 'and' for logical conjunctions
+ // CHECK-FIXES: 1 and 0
+}
+
+void bitwise_and()
+{
+ (void)(0&1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use 'bitand' for bitwise conjunctions
+ // CHECK-FIXES: 0 bitand 1
+
+ (void)(0 &1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use 'bitand' for bitwise conjunctions
+ // CHECK-FIXES: 0 bitand 1
+
+ (void)(0& 1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use 'bitand' for bitwise conjunctions
+ // CHECK-FIXES: 0 bitand 1
+
+ (void)(0 & 1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use 'bitand' for bitwise conjunctions
+ // CHECK-FIXES: 0 bitand 1
+}
+
+void bitwise_or() {
+ (void)(0|1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use 'bitor' for bitwise disjunctions
+ // CHECK-FIXES: 0 bitor 1
+
+ (void)(0| 1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use 'bitor' for bitwise disjunctions
+ // CHECK-FIXES: 0 bitor 1
+
+ (void)(0 |1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use 'bitor' for bitwise disjunctions
+ // CHECK-FIXES: 0 bitor 1
+
+ (void)(0 | 1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use 'bitor' for bitwise disjunctions
+ // CHECK-FIXES: 0 bitor 1
+}
+
+void bitwise_not() {
+ (void)(~0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use 'compl' for bitwise negation
+ // CHECK-FIXES: compl 0
+
+ (void)(~ 0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use 'compl' for bitwise negation
+ // CHECK-FIXES: compl 0
+}
+
+void logical_not() {
+ (void)(!0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use 'not' for logical negation
+ // CHECK-FIXES: not 0
+
+ (void)(! 0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use 'not' for logical negation
+ // CHECK-FIXES: not 0
+}
+
+void logical_or() {
+ (void)(1||0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use 'or' for logical disjunctions
+ // CHECK-FIXES: 1 or 0
+
+ (void)(1|| 0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use 'or' for logical disjunctions
+ // CHECK-FIXES: 1 or 0
+
+ (void)(1 ||0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use 'or' for logical disjunctions
+ // CHECK-FIXES: 1 or 0
+
+ (void)(1 || 0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use 'or' for logical disjunctions
+ // CHECK-FIXES: 1 or 0
+}
+
+void bitwise_xor() {
+ (void)(0^1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use 'xor' for exclusive or
+ // CHECK-FIXES: 0 xor 1
+
+ (void)(0^ 1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use 'xor' for exclusive or
+ // CHECK-FIXES: 0 xor 1
+
+ (void)(0 ^1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use 'xor' for exclusive or
+ // CHECK-FIXES: 0 xor 1
+
+ (void)(0 ^ 1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use 'xor' for exclusive or
+ // CHECK-FIXES: 0 xor 1
+}
Index: clang-tools-extra/test/clang-tidy/checkers/readability-alternative-tokens-no-warn.c
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/readability-alternative-tokens-no-warn.c
@@ -0,0 +1,26 @@
+// RUN: %check_clang_tidy %s readability-alternative-tokens %t
+
+// A note to the reader: the whitespace in this file is important: `1&&0`
+// is lexically three tokens, but `1and0` is lexed as a single
+// identifier. This test needs to make sure that the fixit applies whitespace in
+// its change.
+
+#include <iso646.h>
+
+// clang-format off
+
+void f1()
+{
+ (void)(1 and 0);
+ (void)(0 bitand 1);
+ (void)(0 bitor 1);
+ (void)(compl 0);
+ (void)(not 0);
+ (void)(1 or 0);
+ (void)(0 xor 1);
+
+ // This is here because clang-tidy tests seemingly need at least on CHECK-MESSAGES.
+ (void)(0 && 1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use 'and' for logical conjunctions
+ // CHECK-FIXES: 0 and 1
+}
Index: clang-tools-extra/docs/clang-tidy/checks/readability-alternative-tokens.rst
===================================================================
--- /dev/null
+++ clang-tools-extra/docs/clang-tidy/checks/readability-alternative-tokens.rst
@@ -0,0 +1,86 @@
+.. title:: clang-tidy - readability-alternative-tokens
+
+readability-alternative-tokens
+==================================
+
+Finds uses of symbol-based logical and bitwise operators and recommends using
+alternative tokens instead.
+
+Although symbols are the mainstream tokens in syntaxes derived from C, spelling
+out the operators can be clearer. It more clearly expresses programmer intention
+by reducing barriers for interpretation, makes small operators stand out, makes
+similar tokens more visibly distinguishable, and reduces the chances for typos.
+
+.. code-block:: c++
+
+ // warning: use 'and' for logical conjunctions
+ x && y
+
+ // warning: use 'bitand' for bitwise conjunctions
+ x & y
+
+ // warning: use 'bitor' for bitwise disjunctions
+ x | y
+
+ // warning: use 'compl' for bitwise negation
+ ~x
+
+ // warning: use 'not' for logical negation
+ not x
+
+ // warning: use 'or' for logical disjunctions
+ x || y
+
+ // warning: use 'xor' for exclusive or
+ x ^ y
+
+Supported languages
+===================
+
+* C99 and later
+* C++98 and later
+
+Limitations
+===========
+
+The following limitations are planned to be fixed within a reasonable timeframe.
+
+Configurability
+---------------
+
+A later variant of this patch will support being able to configure how logical
+and bitwise operators are spelt.
+
+Preprocessor support
+--------------------
+
+This does not currently support the preprocessor.
+
+Program composition
+-------------------
+
+This check doesn't yet account for program composition. This means that the
+warning is currently in conflict with piping C++20 ranges, as well as any other
+library that uses ``|`` as a composition operator.
+
+.. code-block:: c++
+
+ // warning: use 'bitor' for bitwise disjunctions
+ auto evens = std::views::iota(0, 1'000)
+ | std::views::filter([](int const x) { return x % 2 == 0; });
+
+ // warning: use 'bitor' for bitwise disjunctions
+ auto value = qi::int_ | qi::bool_;
+
+ // warning: use 'bitor' for bitwise disjunctions
+ std::fstream file("hello.txt", std::ios::in | std::ios::out);
+
+The reason for this limitation is because it's not yet clear how to specify to
+clang-tidy that an ``|`` is being used for composition over a genuine bitwise
+disjunction. Two ideas under consideration are:
+
+1. Have implementers apply an attribute such as ``[[clang::composable]]`` which
+ signals to the linter that ``|`` is preferred.
+
+2. Have clang-tidy users supply a list of types that can be considered as
+ composable.
Index: clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
===================================================================
--- clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
+++ clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
@@ -9,6 +9,7 @@
#include "../ClangTidy.h"
#include "../ClangTidyModule.h"
#include "../ClangTidyModuleRegistry.h"
+#include "AlternativeTokensCheck.h"
#include "AvoidConstParamsInDecls.h"
#include "BracesAroundStatementsCheck.h"
#include "ConstReturnTypeCheck.h"
@@ -55,6 +56,8 @@
class ReadabilityModule : public ClangTidyModule {
public:
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+ CheckFactories.registerCheck<AlternativeTokensCheck>(
+ "readability-alternative-tokens");
CheckFactories.registerCheck<AvoidConstParamsInDecls>(
"readability-avoid-const-params-in-decls");
CheckFactories.registerCheck<BracesAroundStatementsCheck>(
Index: clang-tools-extra/clang-tidy/readability/CMakeLists.txt
===================================================================
--- clang-tools-extra/clang-tidy/readability/CMakeLists.txt
+++ clang-tools-extra/clang-tidy/readability/CMakeLists.txt
@@ -4,6 +4,7 @@
)
add_clang_library(clangTidyReadabilityModule
+ AlternativeTokensCheck.cpp
AvoidConstParamsInDecls.cpp
BracesAroundStatementsCheck.cpp
ConstReturnTypeCheck.cpp
Index: clang-tools-extra/clang-tidy/readability/AlternativeTokensCheck.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/readability/AlternativeTokensCheck.h
@@ -0,0 +1,54 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_USEALTERNATIVETOKENSCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_USEALTERNATIVETOKENSCHECK_H
+
+#include "../ClangTidyCheck.h"
+#include "../utils/IncludeInserter.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/SourceManager.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+/// Flags uses of symbol-based bitwise and logical operators.
+class AlternativeTokensCheck : public ClangTidyCheck {
+public:
+ using ClangTidyCheck::ClangTidyCheck;
+
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.C99 || LangOpts.CPlusPlus;
+ }
+
+ void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
+ Preprocessor *ModuleExpanderPP) override {
+ this->SM = &SM;
+ Includer.registerPreprocessor(PP);
+ }
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ const SourceManager *SM = nullptr;
+ utils::IncludeInserter Includer{
+ Options.getLocalOrGlobal("IncludeStyle", utils::IncludeSorter::IS_LLVM)};
+
+ void checkSpelling(const UnaryOperator &Op);
+ void checkSpelling(const BinaryOperator &Op);
+ void checkSpelling(const CXXOperatorCallExpr &Op);
+
+ Optional<FixItHint> includeIso646(SourceLocation Loc);
+ FixItHint createReplacement(SourceLocation Loc, StringRef S, int N) const;
+ char lookahead(SourceLocation Loc, int N) const;
+};
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_USEALTERNATIVETOKENSCHECK_H
Index: clang-tools-extra/clang-tidy/readability/AlternativeTokensCheck.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/readability/AlternativeTokensCheck.cpp
@@ -0,0 +1,185 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "AlternativeTokensCheck.h"
+#include "../utils/IncludeInserter.h"
+#include "../utils/LexerUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Expr.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/OperatorKinds.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Lex/Preprocessor.h"
+
+#include <cstring>
+
+namespace clang {
+namespace tidy {
+namespace readability {
+namespace {
+AST_MATCHER(UnaryOperator, isBitwiseOrLogicalUnaryOp) {
+ return Node.getOpcode() == UnaryOperator::Opcode::UO_Not ||
+ Node.getOpcode() == UnaryOperator::Opcode::UO_LNot;
+}
+} // namespace
+
+using ast_matchers::binaryOperation;
+using ast_matchers::hasAnyOperatorName;
+using ast_matchers::MatchFinder;
+using ast_matchers::unaryOperator;
+
+void AlternativeTokensCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(unaryOperator(isBitwiseOrLogicalUnaryOp()).bind("unary"),
+ this);
+ Finder->addMatcher(
+ binaryOperation(hasAnyOperatorName("&&", "||", "!", "&", "|", "~", "^"))
+ .bind("operator"),
+ this);
+}
+
+Optional<FixItHint> AlternativeTokensCheck::includeIso646(SourceLocation Loc) {
+ if (!getLangOpts().C99) {
+ return llvm::None;
+ }
+
+ return Includer.createIncludeInsertion(SM->getFileID(Loc), "<iso646.h>");
+}
+
+char AlternativeTokensCheck::lookahead(SourceLocation Loc, int N) const {
+ return *(SM->getCharacterData(Loc.getLocWithOffset(N)));
+}
+
+FixItHint AlternativeTokensCheck::createReplacement(SourceLocation Loc,
+ StringRef S, int N) const {
+ // Only insert spaces if there aren't already spaces between operators
+ StringRef SpaceBefore = std::isspace(lookahead(Loc, -1)) ? "" : " ";
+ StringRef SpaceAfter = std::isspace(lookahead(Loc, N)) ? "" : " ";
+ return FixItHint::CreateReplacement(Loc,
+ (SpaceBefore + S + SpaceAfter).str());
+}
+
+void AlternativeTokensCheck::checkSpelling(const UnaryOperator &Op) {
+ SourceLocation Loc = Op.getOperatorLoc();
+ char First = *(SM->getCharacterData(Loc));
+ if (std::isalpha(First) || Loc.isMacroID())
+ return;
+
+ switch (Op.getOpcode()) {
+ case UnaryOperator::Opcode::UO_Not:
+ diag(Loc, "use 'compl' for bitwise negation")
+ << createReplacement(Loc, "compl", 1) << includeIso646(Loc);
+ return;
+ case UnaryOperator::Opcode::UO_LNot:
+ diag(Loc, "use 'not' for logical negation")
+ << createReplacement(Loc, "not", 1) << includeIso646(Loc);
+ return;
+ default:
+ return;
+ }
+}
+
+void AlternativeTokensCheck::checkSpelling(const BinaryOperator &Op) {
+ SourceLocation Loc = Op.getOperatorLoc();
+ char First = *(SM->getCharacterData(Loc));
+ if (std::isalpha(First) || Loc.isMacroID())
+ return;
+
+ using Opcode = BinaryOperator::Opcode;
+ Opcode OC = Op.getOpcode();
+ switch (OC) {
+ case Opcode::BO_LAnd:
+ diag(Loc, "use 'and' for logical conjunctions")
+ << createReplacement(Loc, "and", 2) << includeIso646(Loc);
+ break;
+ case Opcode::BO_And:
+ diag(Loc, "use 'bitand' for bitwise conjunctions")
+ << createReplacement(Loc, "bitand", 1) << includeIso646(Loc);
+ break;
+ case Opcode::BO_Or:
+ diag(Loc, "use 'bitor' for bitwise disjunctions")
+ << createReplacement(Loc, "bitor", 1) << includeIso646(Loc);
+ break;
+ case Opcode::BO_LOr:
+ diag(Loc, "use 'or' for logical disjunctions")
+ << createReplacement(Loc, "or", 2) << includeIso646(Loc);
+ break;
+ case Opcode::BO_Xor:
+ diag(Loc, "use 'xor' for exclusive or")
+ << createReplacement(Loc, "xor", 1) << includeIso646(Loc);
+ break;
+ default:
+ break;
+ }
+}
+
+void AlternativeTokensCheck::checkSpelling(const CXXOperatorCallExpr &Op) {
+ SourceLocation Loc = Op.getOperatorLoc();
+ char First = *(SM->getCharacterData(Loc));
+ if (std::isalpha(First) || Loc.isMacroID())
+ return;
+
+ using Opcode = OverloadedOperatorKind;
+ constexpr Opcode And = Opcode::OO_AmpAmp;
+ constexpr Opcode Bitand = Opcode::OO_Amp;
+ constexpr Opcode Bitor = Opcode::OO_Pipe;
+ constexpr Opcode Compl = Opcode::OO_Tilde;
+ constexpr Opcode Not = Opcode::OO_Exclaim;
+ constexpr Opcode Or = Opcode::OO_PipePipe;
+ constexpr Opcode Xor = Opcode::OO_Caret;
+
+ Opcode OC = Op.getOperator();
+ switch (OC) {
+ case And:
+ diag(Loc, "use 'and' for logical conjunctions")
+ << createReplacement(Loc, "and", 2) << includeIso646(Loc);
+ break;
+ case Bitand:
+ diag(Loc, "use 'bitand' for bitwise conjunctions")
+ << createReplacement(Loc, "bitand", 1) << includeIso646(Loc);
+ break;
+ case Bitor:
+ diag(Loc, "use 'bitor' for bitwise disjunctions")
+ << createReplacement(Loc, "bitor", 1) << includeIso646(Loc);
+ break;
+ case Compl:
+ diag(Loc, "use 'compl' for bitwise negation")
+ << createReplacement(Loc, "compl", 1) << includeIso646(Loc);
+ break;
+ case Not:
+ diag(Loc, "use 'not' for logical negation")
+ << createReplacement(Loc, "not", 1) << includeIso646(Loc);
+ break;
+ case Or:
+ diag(Loc, "use 'or' for logical disjunctions")
+ << createReplacement(Loc, "or", 2) << includeIso646(Loc);
+ break;
+ case Xor:
+ diag(Loc, "use 'xor' for exclusive or")
+ << createReplacement(Loc, "xor", 1) << includeIso646(Loc);
+ break;
+ default:
+ break;
+ }
+}
+
+void AlternativeTokensCheck::check(const MatchFinder::MatchResult &Result) {
+ if (const auto *Op = Result.Nodes.getNodeAs<UnaryOperator>("unary"))
+ return checkSpelling(*Op);
+
+ if (const auto *Op = Result.Nodes.getNodeAs<BinaryOperator>("operator"))
+ return checkSpelling(*Op);
+
+ if (const auto *Op = Result.Nodes.getNodeAs<CXXOperatorCallExpr>("operator"))
+ return checkSpelling(*Op);
+}
+} // namespace readability
+} // namespace tidy
+} // namespace clang
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits