Index: clang-tidy/misc/RedundantExpressionCheck.cpp =================================================================== --- clang-tidy/misc/RedundantExpressionCheck.cpp +++ clang-tidy/misc/RedundantExpressionCheck.cpp @@ -224,7 +224,7 @@ } // Returns whether the ranges covered by the union of both relational -// expressions covers the whole domain (i.e. x < 10 and x > 0). +// expressions cover the whole domain (i.e. x < 10 and x > 0). static bool rangesFullyCoverDomain(BinaryOperatorKind OpcodeLHS, const APSInt &ValueLHS, BinaryOperatorKind OpcodeRHS, @@ -615,7 +615,7 @@ return true; } -// Checks for expressions like (X == 4) && (Y != 9) +// Checks for expressions like (X == 4) && (Y != 9). static bool areSidesBinaryConstExpressions(const BinaryOperator *&BinOp, const ASTContext *AstCtx) { if (!isa(BinOp->getLHS()) || @@ -632,7 +632,7 @@ } // Retrieves integer constant subexpressions from binary operator expressions -// that have two equivalent sides +// that have two equivalent sides. // E.g.: from (X == 5) && (X == 5) retrieves 5 and 5. static bool retrieveConstExprFromBothSides(const BinaryOperator *&BinOp, BinaryOperatorKind &MainOpcode, @@ -824,6 +824,18 @@ .bind("call"), this); + const auto IneffBitwiseConst = matchIntegerConstantExpr("ineff-bitwise"); + const auto IneffBitwiseSymExpr = matchSymbolicExpr("ineff-bitwise"); + + // Match ineffective or redundant bitwise operator expressions like: x & 0 or x |= ~0. + Finder->addMatcher( + binaryOperator(anyOf(hasOperatorName("|"), hasOperatorName("&"), + hasOperatorName("|="), hasOperatorName("&=")), + hasEitherOperand(IneffBitwiseConst), + hasEitherOperand(IneffBitwiseSymExpr)) + .bind("ineffective-bitwise"), + this); + // Match common expressions and apply more checks to find redundant // sub-expressions. // a) Expr K1 == K2 @@ -932,6 +944,27 @@ } } +static bool exprEvaluatesToZero(BinaryOperatorKind Opcode, APSInt Value){ + if((Opcode == BO_And || Opcode == BO_AndAssign) && Value == 0) + return true; + return false; +} + +static bool exprEvaluatesToNotZero(BinaryOperatorKind Opcode, APSInt Value){ + if((Opcode == BO_Or || Opcode == BO_OrAssign) && (~Value) == 0) + return true; + return false; +} + +static bool exprEvaluatesToSymbolic(BinaryOperatorKind Opcode, APSInt Value){ + if ((Opcode == BO_Or || Opcode == BO_OrAssign) && Value == 0) + return true; + if ((Opcode == BO_And || Opcode == BO_AndAssign) && (~Value) == 0) + return true; + + return false; +} + void RedundantExpressionCheck::checkBitwiseExpr( const MatchFinder::MatchResult &Result) { if (const auto *ComparisonOperator = Result.Nodes.getNodeAs( @@ -965,6 +998,44 @@ 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; + BinaryOperatorKind Opcode = IneffectiveOperator->getOpcode(); + + if (!retrieveSymbolicExpr(Result, "ineff-bitwise", Sym) || + !retrieveIntegerConstantExpr(Result, "ineff-bitwise", Value, ConstExpr)) + return; + + if(Value != 0 && (~Value) != 0) + return; + + SourceLocation Loc = IneffectiveOperator->getOperatorLoc(); + + if (exprEvaluatesToZero(Opcode, Value)) { + diag(Loc, "expression always evaluates to 0"); + } else if (exprEvaluatesToNotZero(Opcode, Value)) { + SourceRange ConstExprRange(ConstExpr->getLocStart(), + ConstExpr->getLocEnd()); + StringRef ConstExprText = Lexer::getSourceText( + CharSourceRange::getTokenRange(ConstExprRange), *Result.SourceManager, + Result.Context->getLangOpts()); + + std::string message = ("expression always evaluates to " + ConstExprText).str(); + diag(Loc, message); + } else if (exprEvaluatesToSymbolic(Opcode, Value)) { + + SourceRange SymExprRange(Sym->getLocStart(), Sym->getLocEnd()); + + StringRef ExprText = Lexer::getSourceText( + CharSourceRange::getTokenRange(SymExprRange), *Result.SourceManager, + Result.Context->getLangOpts()); + + std::string message = ("expression always evaluates to '" + ExprText + "'").str(); + diag(Loc, message); + } } } @@ -1052,6 +1123,7 @@ diag(BinOp->getOperatorLoc(), "both sides of operator are equivalent"); } + if (const auto *CondOp = Result.Nodes.getNodeAs("cond")) { const auto *TrueExpr = CondOp->getTrueExpr(); @@ -1089,8 +1161,10 @@ // Check for the following binded expression: // - "binop-const-compare-to-const", + // - "ineffective-bitwise" // Produced message: // -> "logical expression is always false/true" + // -> "expression always evaluates to ..." checkBitwiseExpr(Result); // Check for te following binded expression: 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;