cjdb created this revision.
cjdb added a reviewer: aaron.ballman.
cjdb requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

C++'s alternative tokens are a good way to improve readability and
minimise bugs by expressing intention using words instead of symbols
(which can both be more easily misspelt _and_ misread).

This warning analyses the binary logical and bitwise operators and warns
if they aren't spelt using the alternative tokens. Bitwise compound
assignment and `!=` operators are not in scope since there's nothing to
misspel or misread.

Future work:

- add a fixit
- add a way for the compiler to detect when the pipe operator (`|`) is not 
being used for a bitwise-or operation
- possibly extend this warning beyond C++

Depends on D107291 <https://reviews.llvm.org/D107291>.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D107294

Files:
  clang/include/clang/Basic/DiagnosticParseKinds.td
  clang/lib/Parse/ParseExpr.cpp
  clang/test/Parser/warn-use-alternative-tokens.c
  clang/test/Parser/warn-use-alternative-tokens.cpp
  clang/test/Sema/vector-gcc-compat.cpp

Index: clang/test/Sema/vector-gcc-compat.cpp
===================================================================
--- clang/test/Sema/vector-gcc-compat.cpp
+++ clang/test/Sema/vector-gcc-compat.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -verify -fsyntax-only -Weverything -Wno-unused-but-set-variable -std=c++11 -triple x86_64-apple-darwin10
+// RUN: %clang_cc1 %s -verify -fsyntax-only -Weverything -Wno-unused-but-set-variable -Wno-use-alternative-tokens-in-expressions -std=c++11 -triple x86_64-apple-darwin10
 
 // Test the compatibility of clang++'s vector extensions with g++'s vector
 // extensions. In comparison to the extensions available in C, the !, ?:, && and
Index: clang/test/Parser/warn-use-alternative-tokens.cpp
===================================================================
--- /dev/null
+++ clang/test/Parser/warn-use-alternative-tokens.cpp
@@ -0,0 +1,86 @@
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -Wuse-alternative-tokens-in-expressions -verify %s
+
+void f1()
+{
+  (void)(true && false); // expected-warning{{use 'and' for logical conjunctions}}
+  (void)(true and false); // no warning
+
+  (void)(0 & 1); // expected-warning{{use 'bitand' for bitwise conjunctions}}
+  (void)(0 bitand 1); // no warning
+
+  (void)(0 | 1); // expected-warning{{use 'bitor' for bitwise disjunctions}}
+  (void)(0 bitor 1); // no warning
+
+  (void)(~0); // expected-warning{{use 'compl' for bitwise negation}}
+  (void)(compl 0); // no warning
+
+  (void)(!false); // expected-warning{{use 'not' for logical negation}}
+  (void)(not false); // no warning
+
+  (void)(false || true); // expected-warning{{use 'or' for logical disjunctions}}
+  (void)(false or true); // no warning
+
+  (void)(0 ^ 1); // expected-warning{{use 'xor' for exclusive or}}
+  (void)(0 xor 1); // no warning
+}
+
+template<class T>
+requires true && false // expected-warning{{use 'and' for logical conjunctions}}
+void f2();
+
+template<class T>
+requires true and false // no warning
+void f2();
+
+template<class T>
+requires true || false // expected-warning{{use 'or' for logical disjunctions}}
+void f3();
+
+template<class T>
+requires true or false // no warning
+void f3();
+
+template<class T>
+requires (!true) // expected-warning{{use 'not' for logical negation}}
+void f4();
+
+template<class T>
+requires (not true) // no warning
+void f4();
+
+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 f5()
+{
+  S x;
+  S y;
+
+  (void)(x && y); // expected-warning{{use 'and' for logical conjunctions}}
+  (void)(x and y); // no warning
+
+  (void)(x & y); // expected-warning{{use 'bitand' for bitwise conjunctions}}
+  (void)(x bitand y); // no warning
+
+  (void)(x | y); // expected-warning{{use 'bitor' for bitwise disjunctions}}
+  (void)(x bitor y); // no warning
+
+  (void)(~x); // expected-warning{{use 'compl' for bitwise negation}}
+  (void)(compl x); // no warning
+
+  (void)(!x); // expected-warning{{use 'not' for logical negation}}
+  (void)(not x); // no warning
+
+  (void)(x || y); // expected-warning{{use 'or' for logical disjunctions}}
+  (void)(x or y); // no warning
+
+  (void)(x ^ y); // expected-warning{{use 'xor' for exclusive or}}
+  (void)(x xor y); // no warning
+}
Index: clang/test/Parser/warn-use-alternative-tokens.c
===================================================================
--- /dev/null
+++ clang/test/Parser/warn-use-alternative-tokens.c
@@ -0,0 +1,14 @@
+// RUN: %clang_cc1 -fsyntax-only -Wuse-alternative-tokens-in-expressions -verify %s
+
+// expected-no-diagnostics
+
+void f1()
+{
+  (void)(1 && 0);
+  (void)(0 & 1);
+  (void)(0 | 1);
+  (void)(~0);
+  (void)(!0);
+  (void)(0 || 1);
+  (void)(0 ^ 1);
+}
Index: clang/lib/Parse/ParseExpr.cpp
===================================================================
--- clang/lib/Parse/ParseExpr.cpp
+++ clang/lib/Parse/ParseExpr.cpp
@@ -317,6 +317,13 @@
   if (LHS.isInvalid())
     return ExprError();
   while (Tok.is(tok::ampamp)) {
+    if (Tok.getSpelling() == "&&") {
+      constexpr int And = 0;
+      constexpr int Logical = 2;
+      constexpr int Conjunctions = 0;
+      Diag(Tok.getLocation(), diag::warn_use_alternative_tokens_in_expressions)
+          << And << Logical << Conjunctions;
+    }
     SourceLocation LogicalAndLoc = ConsumeToken();
     ExprResult RHS = ParsePrimary();
     if (RHS.isInvalid()) {
@@ -351,6 +358,13 @@
   if (!LHS.isUsable())
     return ExprError();
   while (Tok.is(tok::pipepipe)) {
+    if (Tok.getSpelling() == "||") {
+      constexpr int Or = 5;
+      constexpr int Logical = 2;
+      constexpr int Disjunctions = 1;
+      Diag(Tok.getLocation(), diag::warn_use_alternative_tokens_in_expressions)
+          << Or << Logical << Disjunctions;
+    }
     SourceLocation LogicalOrLoc = ConsumeToken();
     ExprResult RHS =
         ParseConstraintLogicalAndExpression(IsTrailingRequiresClause);
@@ -390,6 +404,78 @@
   return isFoldOperator(getBinOpPrecedence(Kind, GreaterThanIsOperator, true));
 }
 
+void CheckLogicalOperators(Parser &P, const Token &Tok) {
+  if (Tok.is(tok::ampamp) && Tok.getSpelling() == "&&") {
+    constexpr int And = 0;
+    constexpr int Logical = 2;
+    constexpr int Conjunctions = 0;
+    P.Diag(Tok.getLocation(), diag::warn_use_alternative_tokens_in_expressions)
+        << And << Logical << Conjunctions;
+    return;
+  }
+
+  if (Tok.is(tok::pipepipe) && Tok.getSpelling() == "||") {
+    constexpr int Or = 5;
+    constexpr int Logical = 2;
+    constexpr int Disjunctions = 1;
+    P.Diag(Tok.getLocation(), diag::warn_use_alternative_tokens_in_expressions)
+        << Or << Logical << Disjunctions;
+    return;
+  }
+}
+
+void CheckBitwiseOperators(Parser &P, const Token &Tok) {
+  if (Tok.is(tok::amp) && Tok.getSpelling() == "&") {
+    constexpr int Bitand = 1;
+    constexpr int Bitwise = 1;
+    constexpr int Conjunctions = 0;
+    P.Diag(Tok.getLocation(), diag::warn_use_alternative_tokens_in_expressions)
+        << Bitand << Bitwise << Conjunctions;
+    return;
+  }
+
+  if (Tok.is(tok::pipe) && Tok.getSpelling() == "|") {
+    // TODO: add Sema checking to allow the pipe operator to be usable for
+    // program composition. This will probably need to happen via an attribute
+    // (e.g. [[clang::composable]]).
+    constexpr int Bitor = 2;
+    constexpr int Bitwise = 1;
+    constexpr int Disjunctions = 1;
+    P.Diag(Tok.getLocation(), diag::warn_use_alternative_tokens_in_expressions)
+        << Bitor << Bitwise << Disjunctions;
+    return;
+  }
+
+  if (Tok.is(tok::caret) && Tok.getSpelling() == "^") {
+    constexpr int Xor = 6;
+    constexpr int Bitwise = 0;
+    constexpr int ExclusiveOr = 2;
+    P.Diag(Tok.getLocation(), diag::warn_use_alternative_tokens_in_expressions)
+        << Xor << Bitwise << ExclusiveOr;
+    return;
+  }
+}
+
+void CheckNegationOperators(Parser &P, const Token &Tok) {
+  if (Tok.is(tok::exclaim) && Tok.getSpelling() == "!") {
+    constexpr int Not = 4;
+    constexpr int Logical = 2;
+    constexpr int Negation = 3;
+    P.Diag(Tok.getLocation(), diag::warn_use_alternative_tokens_in_expressions)
+        << Not << Logical << Negation;
+    return;
+  }
+
+  if (Tok.is(tok::tilde) && Tok.getSpelling() == "~") {
+    constexpr int Compl = 3;
+    constexpr int Bitwise = 1;
+    constexpr int Negation = 3;
+    P.Diag(Tok.getLocation(), diag::warn_use_alternative_tokens_in_expressions)
+        << Compl << Bitwise << Negation;
+    return;
+  }
+}
+
 /// Parse a binary expression that starts with \p LHS and has a
 /// precedence of at least \p MinPrec.
 ExprResult
@@ -435,6 +521,11 @@
       return LHS;
     }
 
+    if (getLangOpts().CPlusPlus) {
+      CheckLogicalOperators(*this, OpToken);
+      CheckBitwiseOperators(*this, OpToken);
+    }
+
     // If the next token is an ellipsis, then this is a fold-expression. Leave
     // it alone so we can handle it in the paren expression.
     if (isFoldOperator(NextTokPrec) && Tok.is(tok::ellipsis)) {
@@ -1374,6 +1465,10 @@
   case tok::kw___imag: {   // unary-expression: '__imag' cast-expression [GNU]
     if (NotPrimaryExpression)
       *NotPrimaryExpression = true;
+
+    if (getLangOpts().CPlusPlus)
+      CheckNegationOperators(*this, Tok);
+
     SourceLocation SavedLoc = ConsumeToken();
     PreferredType.enterUnary(Actions, Tok.getLocation(), SavedKind, SavedLoc);
     Res = ParseCastExpression(AnyCastExpr);
Index: clang/include/clang/Basic/DiagnosticParseKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticParseKinds.td
+++ clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1547,4 +1547,10 @@
 
 def note_max_tokens_total_override : Note<"total token limit set here">;
 
+def warn_use_alternative_tokens_in_expressions : Warning<
+  "use '%select{and|bitand|bitor|compl|not|or|xor}0' "
+  "for %select{|bitwise |logical }1"
+  "%select{conjunctions|disjunctions|exclusive or|negation}2">,
+  InGroup<DiagGroup<"use-alternative-tokens-in-expressions">>, DefaultIgnore;
+
 } // end of Parser diagnostics
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to