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,15 @@ "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 note_xor_used_as_pow_silence : Note< + "replace expression with '%0' to silence this warning">; + 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,63 @@ 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; + } + + const llvm::APInt &LeftSideValue = LHSInt->getValue(); + const llvm::APInt &RightSideValue = RHSInt->getValue(); + const int64_t RightSideIntValue = RightSideValue.getSExtValue(); + if (RightSideIntValue < 1) + return; + + if (LeftSideValue == 2 || LeftSideValue == 10) { + llvm::APInt XorValue = LeftSideValue ^ RightSideValue; + std::string LHSStr = Lexer::getSourceText( + CharSourceRange::getTokenRange(LHSInt->getSourceRange()), + S.getSourceManager(), S.getLangOpts()); + std::string RHSStr = Lexer::getSourceText( + CharSourceRange::getTokenRange(RHSInt->getSourceRange()), + S.getSourceManager(), S.getLangOpts()); + if (LeftSideValue == 2) { + llvm::APInt PowValue = (LeftSideValue - 1) << RightSideValue; + std::string SuggestedExpr = "1<<" + RHSStr; + 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 (LeftSideValue == 10) { + std::string SuggestedValue = "1" + std::string(RightSideIntValue, '0'); + S.Diag(Loc, diag::warn_xor_used_as_pow_base_ten) + << ExprStr << XorValue.toString(10, true) << SuggestedValue + << FixItHint::CreateReplacement(OpRange, SuggestedValue); + } + + S.Diag(Loc, diag::note_xor_used_as_pow_silence) + << ("(" + LHSStr + ") ^ " + RHSStr); + } +} + QualType Sema::CheckVectorLogicalOperands(ExprResult &LHS, ExprResult &RHS, SourceLocation Loc) { // Ensure that either both operands are of the same vector type, or @@ -10933,6 +10990,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,52 @@ +// 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" + // expected-note@-2 {{replace expression with '(2) ^ 1' to silence this warning}} + 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" + // expected-note@-2 {{replace expression with '(2) ^ 8' to silence this warning}} + 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" + // expected-note@-2 {{replace expression with '(TWO) ^ 8' to silence this warning}} + 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" + // expected-note@-2 {{replace expression with '(2) ^ 16' to silence this warning}} + res = 2 ^ TEN; // expected-warning {{result of '2 ^ TEN' is 8, maybe you mean '1<