Index: include/clang/Basic/DiagnosticGroups.td =================================================================== --- include/clang/Basic/DiagnosticGroups.td +++ include/clang/Basic/DiagnosticGroups.td @@ -505,6 +505,7 @@ def GNUUnionCast : DiagGroup<"gnu-union-cast">; def GNUVariableSizedTypeNotAtEnd : DiagGroup<"gnu-variable-sized-type-not-at-end">; def Varargs : DiagGroup<"varargs">; +def XorAsPow : DiagGroup<"xor-as-pow">; def Unsequenced : DiagGroup<"unsequenced">; // GCC name for -Wunsequenced Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -3302,6 +3302,14 @@ "code; pointer may be assumed to always convert to true">, InGroup; +def warn_xor_used_as_pow_base_two : Warning< + "result of '%0' is %1, maybe you mean '%2' (%3)?">, + InGroup; + +def warn_xor_used_as_pow_base_ten : Warning< + "result of '%0' is %1, maybe you mean '%2'?">, + InGroup; + def warn_null_pointer_compare : Warning< "comparison of %select{address of|function|array}0 '%1' %select{not |}2" "equal to a null pointer is always %select{true|false}2">, Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -10890,6 +10890,50 @@ return GetSignedVectorType(vType); } +static void diagnoseXorMisusedAsPow(Sema &S, ExprResult &LHS, ExprResult &RHS, + SourceLocation Loc) { + auto *LHSInt = dyn_cast(LHS.get()); + auto *RHSInt = dyn_cast(RHS.get()); + if (!LHSInt || !RHSInt) + return; + + if (LHSInt->getValue().getBitWidth() != RHSInt->getValue().getBitWidth()) + return; + + CharSourceRange OpRange = CharSourceRange::getCharRange( + LHSInt->getBeginLoc(), S.getLocForEndOfToken(RHSInt->getLocation())); + llvm::StringRef ExprStr = + Lexer::getSourceText(OpRange, S.getSourceManager(), S.getLangOpts()); + + if (S.getLangOpts().CPlusPlus) { + // Do not diagnose binary literals + if (ExprStr.find("0b") != llvm::StringRef::npos) + return; + // Do not diagnose if xor keyword is used + if (ExprStr.find("xor") != llvm::StringRef::npos) + return; + } + + int64_t RightSideValue = RHSInt->getValue().getSExtValue(); + if (RightSideValue < 1) + return; + + llvm::APInt XorValue = LHSInt->getValue() ^ RHSInt->getValue(); + if (LHSInt->getValue() == 2) { + llvm::APInt PowValue = (LHSInt->getValue() - 1) << RHSInt->getValue(); + std::string SuggestedExpr = "1<<" + RHSInt->getValue().toString(10, true); + S.Diag(Loc, diag::warn_xor_used_as_pow_base_two) + << ExprStr << XorValue.toString(10, true) << SuggestedExpr + << PowValue.toString(10, true) + << FixItHint::CreateReplacement(OpRange, SuggestedExpr); + } else if (LHSInt->getValue() == 10) { + std::string SuggestedValue = "1" + std::string(RightSideValue, '0'); + S.Diag(Loc, diag::warn_xor_used_as_pow_base_ten) + << ExprStr << XorValue.toString(10, true) << SuggestedValue + << FixItHint::CreateReplacement(OpRange, SuggestedValue); + } +} + QualType Sema::CheckVectorLogicalOperands(ExprResult &LHS, ExprResult &RHS, SourceLocation Loc) { // Ensure that either both operands are of the same vector type, or @@ -10933,6 +10977,9 @@ if (Opc == BO_And) diagnoseLogicalNotOnLHSofCheck(*this, LHS, RHS, Loc, Opc); + if (Opc == BO_Xor) + diagnoseXorMisusedAsPow(*this, LHS, RHS, Loc); + ExprResult LHSResult = LHS, RHSResult = RHS; QualType compType = UsualArithmeticConversions(LHSResult, RHSResult, IsCompAssign); Index: test/SemaCXX/warn-xor-as-pow.cpp =================================================================== --- test/SemaCXX/warn-xor-as-pow.cpp +++ test/SemaCXX/warn-xor-as-pow.cpp @@ -0,0 +1,37 @@ +// RUN: %clang_cc1 -x c++ -fsyntax-only -verify -Wxor-as-pow %s +// RUN: %clang_cc1 -x c++ -fsyntax-only -verify %s +// RUN: %clang_cc1 -x c++ -fsyntax-only -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s + +#define TWO 2 +#define TEN 10 +#define TWO_ULL 2ULL +void test(unsigned a, unsigned b) { + unsigned res; + res = a ^ 5; + res = 2 ^ b; + res = a ^ b; + res = 2 ^ 0; + res = 2 ^ 1; // expected-warning {{result of '2 ^ 1' is 3, maybe you mean '1<<1' (2)?}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:11-[[@LINE-1]]:16}:"1<<1" + res = 2 ^ 8; // expected-warning {{result of '2 ^ 8' is 10, maybe you mean '1<<8' (256)?}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:11-[[@LINE-1]]:16}:"1<<8" + res = TWO ^ 8; // expected-warning {{result of 'TWO ^ 8' is 10, maybe you mean '1<<8' (256)?}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:11-[[@LINE-1]]:18}:"1<<8" + res = 2 ^ 16; // expected-warning {{result of '2 ^ 16' is 18, maybe you mean '1<<16' (65536)?}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:11-[[@LINE-1]]:17}:"1<<16" + res = 0b10 ^ 16; + res = 2 ^ 0b100; + res = 2 xor 16; + unsigned char two = 2; + res = two ^ 16; + res = TWO_ULL ^ 16; + + res = 10 ^ 0; + res = 10 ^ 1; // expected-warning {{result of '10 ^ 1' is 11, maybe you mean '10'?}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:11-[[@LINE-1]]:17}:"10" + res = 10 ^ 10; // expected-warning {{result of '10 ^ 10' is 0, maybe you mean '10000000000'?}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:11-[[@LINE-1]]:18}:"10000000000" + res = TEN ^ 10; // expected-warning {{result of 'TEN ^ 10' is 0, maybe you mean '10000000000'?}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:11-[[@LINE-1]]:19}:"10000000000" + res = 10 xor 10; +}