diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/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>, DefaultIgnore; + } // end of Parser diagnostics diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp --- a/clang/lib/Parse/ParseExpr.cpp +++ b/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); diff --git a/clang/test/Parser/warn-use-alternative-tokens.c b/clang/test/Parser/warn-use-alternative-tokens.c new file mode 100644 --- /dev/null +++ b/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); +} diff --git a/clang/test/Parser/warn-use-alternative-tokens.cpp b/clang/test/Parser/warn-use-alternative-tokens.cpp new file mode 100644 --- /dev/null +++ b/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 +requires true && false // expected-warning{{use 'and' for logical conjunctions}} +void f2(); + +template +requires true and false // no warning +void f2(); + +template +requires true || false // expected-warning{{use 'or' for logical disjunctions}} +void f3(); + +template +requires true or false // no warning +void f3(); + +template +requires (!true) // expected-warning{{use 'not' for logical negation}} +void f4(); + +template +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 +} diff --git a/clang/test/Sema/vector-gcc-compat.cpp b/clang/test/Sema/vector-gcc-compat.cpp --- a/clang/test/Sema/vector-gcc-compat.cpp +++ b/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