Index: clang-tidy/misc/RedundantExpressionCheck.cpp =================================================================== --- clang-tidy/misc/RedundantExpressionCheck.cpp +++ clang-tidy/misc/RedundantExpressionCheck.cpp @@ -518,9 +518,6 @@ if (!OverloadedFunctionDecl) return false; - if (canOverloadedOperatorArgsBeModified(OverloadedFunctionDecl, false)) - return false; - if (!OverloadedOperatorExpr->getArg(1)->isIntegerConstantExpr( Value, *Result.Context)) return false; @@ -677,6 +674,15 @@ .bind("call"), this); + // Match ineffective bitwise operator expressions like: x & 0 or x |= ~0. + Finder->addMatcher( + binaryOperator(anyOf(hasOperatorName("|"), hasOperatorName("&"), + hasOperatorName("|="), hasOperatorName("&=")), + hasEitherOperand(matchIntegerConstantExpr("ineffective-bitwise")), + hasEitherOperand(matchSymbolicExpr("ineffective-bitwise"))) + .bind("ineffective-bitwise"), + this); + // Match common expressions and apply more checks to find redundant // sub-expressions. // a) Expr K1 == K2 @@ -785,6 +791,20 @@ } } +static bool exprEvaluatesToZero(BinaryOperatorKind Opcode, APSInt Value) { + return (Opcode == BO_And || Opcode == BO_AndAssign) && Value == 0; +} + +static bool exprEvaluatesToBitwiseNegatedZero(BinaryOperatorKind Opcode, + APSInt Value) { + return (Opcode == BO_Or || Opcode == BO_OrAssign) && ~Value == 0; +} + +static bool exprEvaluatesToSymbolic(BinaryOperatorKind Opcode, APSInt Value) { + return ((Opcode == BO_Or || Opcode == BO_OrAssign) && Value == 0) || + ((Opcode == BO_And || Opcode == BO_AndAssign) && ~Value == 0); +} + void RedundantExpressionCheck::checkBitwiseExpr( const MatchFinder::MatchResult &Result) { if (const auto *ComparisonOperator = Result.Nodes.getNodeAs( @@ -818,6 +838,42 @@ else if (Opcode == BO_NE) diag(Loc, "logical expression is always true"); } + } else if (const auto *IneffectiveOperator = + Result.Nodes.getNodeAs( + "ineffective-bitwise")) { + APSInt Value; + const Expr *Sym = nullptr, *ConstExpr = nullptr; + + if (!retrieveSymbolicExpr(Result, "ineffective-bitwise", Sym) || + !retrieveIntegerConstantExpr(Result, "ineffective-bitwise", Value, ConstExpr)) + return; + + if((Value != 0 && ~Value != 0) || Sym->getExprLoc().isMacroID()) + return; + + SourceLocation Loc = IneffectiveOperator->getOperatorLoc(); + + BinaryOperatorKind Opcode = IneffectiveOperator->getOpcode(); + if (exprEvaluatesToZero(Opcode, Value)) { + diag(Loc, "expression always evaluates to 0"); + } else if (exprEvaluatesToBitwiseNegatedZero(Opcode, Value)) { + SourceRange ConstExprRange(ConstExpr->getLocStart(), + ConstExpr->getLocEnd()); + std::string ConstExprText = Lexer::getSourceText( + CharSourceRange::getTokenRange(ConstExprRange), *Result.SourceManager, + Result.Context->getLangOpts()); + + diag(Loc, + "expression always evaluates to '" + ConstExprText + "'"); + } else if (exprEvaluatesToSymbolic(Opcode, Value)) { + SourceRange SymExprRange(Sym->getLocStart(), Sym->getLocEnd()); + + std::string ExprText = Lexer::getSourceText( + CharSourceRange::getTokenRange(SymExprRange), *Result.SourceManager, + Result.Context->getLangOpts()); + + diag(Loc, "expression always evaluates to '" + ExprText + "'"); + } } } Index: docs/clang-tidy/checks/misc-redundant-expression.rst =================================================================== --- docs/clang-tidy/checks/misc-redundant-expression.rst +++ docs/clang-tidy/checks/misc-redundant-expression.rst @@ -13,7 +13,9 @@ - always ``false``, -- always a constant (zero or one). +- always a constant (zero or one), + +- ineffective, the operation does not change the expression. Examples: @@ -23,3 +25,5 @@ (p->x == p->x) // always true (p->x < p->x) // always false (speed - speed + 1 == 12) // speed - speed is always zero + x |= 0 // operation does not change the value of x + x &= ~0 // operation does not change the value of x Index: test/clang-tidy/misc-redundant-expression.cpp =================================================================== --- test/clang-tidy/misc-redundant-expression.cpp +++ test/clang-tidy/misc-redundant-expression.cpp @@ -268,6 +268,42 @@ } int TestBitwise(int X, int Y) { + if(0 & X) return 1; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: expression always evaluates to 0 + if(~0 | X) return 1; + // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression always evaluates to '~0' + + if(X & 0) return 1; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: expression always evaluates to 0 + if(X | ~0) return 1; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: expression always evaluates to '~0' + if(X | 0xffffffff) return 1; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: expression always evaluates to '0xffffffff' + + if(~0 & X) return 1; + // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression always evaluates to 'X' + if(0 | X) return 1; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: expression always evaluates to 'X' + + if(X & ~0) return 1; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: expression always evaluates to 'X' + if(X | 0) return 1; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: expression always evaluates to 'X' + + X &= ~0; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: expression always evaluates to 'X' + X |= 0; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: expression always evaluates to 'X' + + X &= 0; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: expression always evaluates to 0 + X |= ~0; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: expression always evaluates to '~0' + X |= 0xffffffff; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: expression always evaluates to '0xffffffff' + X |= -1; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: expression always evaluates to '-1' + if ((X & 0xFF) == 0xF00) return 1; // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: logical expression is always false if ((X & 0xFF) != 0xF00) return 1;