Hi, Daniel. Can you split these two checks up into separate patches? I know they're pretty similar and have the same basic idea, but we like to have commits be limited to one new feature at a time. (Combining bitwise and logical operators into one is fine, though.) Sorry it's a bit more work for you to tease them apart.
Some comments: +static bool isIdenticalStmt(const ASTContext &Ctx, const Stmt *Expr1, + const Stmt *Expr2, bool IgnoreSideEffects = false); I think it's probably worth changing the argument names too (to not say "Expr"). + void checkComparissionOp(const BinaryOperator *B); Typo: "Comparison" + "Usage of identical expressions", I would just say "Use" + // Split operators as long as we still have operators to split on. We will + // get called for every binary operator in an expression so there is no need + // to check every one against each other here, just the right most one with + // the others. Clever! Too bad it comes out to an N^2 algorithm, but N is likely pretty small most of the time, and the nature of isIdenticalStmt + while (true) { + const BinaryOperator *B2 = dyn_cast<BinaryOperator>(LHS); + if (!B2) + break; You can combine these into a single line: while (const BinaryOperator *B2 = dyn_cast<BinaryOperator>(LHS)) + if (Stmt1->getStmtClass() == Stmt::CompoundStmtClass) { + const CompoundStmt *CompStmt = cast<CompoundStmt>(Stmt1); This is written: if (const CompoundStmt *CompStmt = dyn_cast<CompoundStmt>(Stmt1)) (Also, good job handling this case!) + "Identical expressions regardless of boolean value", I'm not sure about this one. For one thing, the if-branch and else-branch aren't usually just expressions. How about just "Identical branches"? (Or you/we can continue hunting for a better alternative.) Some missing test cases: Should not warn: x == 4 && y == 5 || x == 4 && y == 6 Should warn: x ? "abc" : "abc" Test cases with while, do, for, if, and return within the branches of an if. If you add the code, we need to test them! We could also not worry about this for now and just say the checker isn't powerful enough to handle them. Why would that be useful? Because I'm a bit concerned about performance for the if-branch-check. Can you try running this over a large-ish project and let me know the before and after times of analyzing with this checker on? If it's a significant difference we should put the if-check under a separate checker name. Thanks for working on this! Jordan On Jan 29, 2014, at 23:30 , Daniel Fahlgren <dan...@fahlgren.se> wrote: > Hi, > > This patch extends the identical expression checker to find more places > where you have code duplication, or copy and paste errors. It will now > find if you check the same thing multiple times in a logical expression. > E.g. > > if (a == 1 || a == 1) > > It will also detect if you have used the same symbol multiple times when > using bitwise operators. E.g. > > int b = a | a; > > The last addition is that it checks if both branches in an if-statement > are identical. E.g. > > if (...) > a = 1; > else > a = 1; > > > Since this is my first patch I'm sure I've forgotten something. Feedback > is welcome. > > > For revision 200460 of the llvm tree it actually gives the following > warnings: > > llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp:428:54: warning: > identical expressions on both sides of bitwise operator > result |= (icmp_eq ? (FoldMskICmp_Mask_AllZeroes | > ~~~~~~~~~~~~~~~~~~~~~~~~~~ ^ > llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp:432:57: warning: > identical expressions on both sides of bitwise operator > : (FoldMskICmp_Mask_NotAllZeroes | > ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^ > > llvm/lib/IR/ConstantFold.cpp:936:12: warning: true and false branches > are identical > else if (isa<UndefValue>(C1)) > ^ > > llvm/tools/clang/lib/Basic/Targets.cpp:981:22: warning: identical > expressions on both sides of bitwise operator > | ArchDefinePwr6 | ArchDefinePpcgr | > ArchDefinePpcsq) > ^ ~~~~~~~~~~~~~~ > llvm/tools/clang/lib/Basic/Targets.cpp:995:24: warning: identical > expressions on both sides of bitwise operator > | ArchDefinePwr6 | ArchDefinePpcgr | > ArchDefinePpcsq) > ^ ~~~~~~~~~~~~~~ > > > Best regards, > Daniel Fahlgren
Index: lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp (revision 200459) +++ lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp (working copy) @@ -26,8 +26,8 @@ using namespace clang; using namespace ento; -static bool isIdenticalExpr(const ASTContext &Ctx, const Expr *Expr1, - const Expr *Expr2, bool IgnoreSideEffects = false); +static bool isIdenticalStmt(const ASTContext &Ctx, const Stmt *Expr1, + const Stmt *Expr2, bool IgnoreSideEffects = false); //===----------------------------------------------------------------------===// // FindIdenticalExprVisitor - Identify nodes using identical expressions. //===----------------------------------------------------------------------===// @@ -39,20 +39,129 @@ explicit FindIdenticalExprVisitor(BugReporter &B, AnalysisDeclContext *A) : BR(B), AC(A) {} // FindIdenticalExprVisitor only visits nodes - // that are binary operators or conditional operators. + // that are binary operators, if statements or + // conditional operators. bool VisitBinaryOperator(const BinaryOperator *B); + bool VisitIfStmt(const IfStmt *I); bool VisitConditionalOperator(const ConditionalOperator *C); private: BugReporter &BR; AnalysisDeclContext *AC; + + void reportIdenticalExpr(const BinaryOperator *B, bool CheckBitwise, + ArrayRef<SourceRange> Sr); + void checkBitwiseOrLogicalOp(const BinaryOperator *B, bool CheckBitwise); + void checkComparissionOp(const BinaryOperator *B); }; } // end anonymous namespace +void FindIdenticalExprVisitor::reportIdenticalExpr(const BinaryOperator *B, + bool CheckBitwise, + ArrayRef<SourceRange> Sr) { + StringRef Message; + if (CheckBitwise) + Message = "identical expressions on both sides of bitwise operator"; + else + Message = "identical expressions on both sides of logical operator"; + + PathDiagnosticLocation ELoc = + PathDiagnosticLocation::createOperatorLoc(B, BR.getSourceManager()); + BR.EmitBasicReport(AC->getDecl(), + "Usage of identical expressions", + categories::LogicError, + Message, ELoc, Sr); +} + +void FindIdenticalExprVisitor::checkBitwiseOrLogicalOp(const BinaryOperator *B, + bool CheckBitwise) { + SourceRange Sr[2]; + + const Expr *LHS = B->getLHS(); + const Expr *RHS = B->getRHS(); + + // Split operators as long as we still have operators to split on. We will + // get called for every binary operator in an expression so there is no need + // to check every one against each other here, just the right most one with + // the others. + while (true) { + const BinaryOperator *B2 = dyn_cast<BinaryOperator>(LHS); + if (!B2) + break; + if (CheckBitwise && !B2->isBitwiseOp()) + break; + if (!CheckBitwise && !B2->isLogicalOp()) + break; + if (isIdenticalStmt(AC->getASTContext(), RHS, B2->getRHS())) { + Sr[0] = RHS->getSourceRange(); + Sr[1] = B2->getRHS()->getSourceRange(); + reportIdenticalExpr(B, CheckBitwise, Sr); + } + LHS = B2->getLHS(); + } + if (isIdenticalStmt(AC->getASTContext(), RHS, LHS)) { + Sr[0] = RHS->getSourceRange(); + Sr[1] = LHS->getSourceRange(); + reportIdenticalExpr(B, CheckBitwise, Sr); + } +} + +bool FindIdenticalExprVisitor::VisitIfStmt(const IfStmt *I) { + const Stmt *Stmt1 = I->getThen(); + const Stmt *Stmt2 = I->getElse(); + + if (!Stmt1 || !Stmt2) + return true; + + // Special handling for code like: + // + // if (b) { + // i = 1; + // } else + // i = 1; + if (Stmt1->getStmtClass() == Stmt::CompoundStmtClass) { + const CompoundStmt *CompStmt = cast<CompoundStmt>(Stmt1); + if (CompStmt->size() == 1) + Stmt1 = CompStmt->body_back(); + } + if (Stmt2->getStmtClass() == Stmt::CompoundStmtClass) { + const CompoundStmt *CompStmt = cast<CompoundStmt>(Stmt2); + if (CompStmt->size() == 1) + Stmt2 = CompStmt->body_back(); + } + + if (isIdenticalStmt(AC->getASTContext(), Stmt1, Stmt2, true)) { + PathDiagnosticLocation ELoc = + PathDiagnosticLocation::createBegin(I, BR.getSourceManager(), AC); + BR.EmitBasicReport(AC->getDecl(), + "Identical expressions regardless of boolean value", + categories::LogicError, + "true and false branches are identical", ELoc); + } + return true; +} + bool FindIdenticalExprVisitor::VisitBinaryOperator(const BinaryOperator *B) { BinaryOperator::Opcode Op = B->getOpcode(); - if (!BinaryOperator::isComparisonOp(Op)) - return true; + + if (BinaryOperator::isBitwiseOp(Op)) + checkBitwiseOrLogicalOp(B, true); + + if (BinaryOperator::isLogicalOp(Op)) + checkBitwiseOrLogicalOp(B, false); + + if (BinaryOperator::isComparisonOp(Op)) + checkComparissionOp(B); + + // We want to visit ALL nodes (subexpressions of binary comparison + // expressions too) that contains comparison operators. + // True is always returned to traverse ALL nodes. + return true; +} + +void FindIdenticalExprVisitor::checkComparissionOp(const BinaryOperator *B) { + BinaryOperator::Opcode Op = B->getOpcode(); + // // Special case for floating-point representation. // @@ -85,26 +194,26 @@ (DeclRef2->getType()->hasFloatingRepresentation())) { if (DeclRef1->getDecl() == DeclRef2->getDecl()) { if ((Op == BO_EQ) || (Op == BO_NE)) { - return true; + return; } } } } else if ((FloatLit1) && (FloatLit2)) { if (FloatLit1->getValue().bitwiseIsEqual(FloatLit2->getValue())) { if ((Op == BO_EQ) || (Op == BO_NE)) { - return true; + return; } } } else if (LHS->getType()->hasFloatingRepresentation()) { // If any side of comparison operator still has floating-point // representation, then it's an expression. Don't warn. // Here only LHS is checked since RHS will be implicit casted to float. - return true; + return; } else { // No special case with floating-point representation, report as usual. } - if (isIdenticalExpr(AC->getASTContext(), B->getLHS(), B->getRHS())) { + if (isIdenticalStmt(AC->getASTContext(), B->getLHS(), B->getRHS())) { PathDiagnosticLocation ELoc = PathDiagnosticLocation::createOperatorLoc(B, BR.getSourceManager()); StringRef Message; @@ -115,10 +224,6 @@ BR.EmitBasicReport(AC->getDecl(), "Compare of identical expressions", categories::LogicError, Message, ELoc); } - // We want to visit ALL nodes (subexpressions of binary comparison - // expressions too) that contains comparison operators. - // True is always returned to traverse ALL nodes. - return true; } bool FindIdenticalExprVisitor::VisitConditionalOperator( @@ -127,7 +232,7 @@ // Check if expressions in conditional expression are identical // from a symbolic point of view. - if (isIdenticalExpr(AC->getASTContext(), C->getTrueExpr(), + if (isIdenticalStmt(AC->getASTContext(), C->getTrueExpr(), C->getFalseExpr(), true)) { PathDiagnosticLocation ELoc = PathDiagnosticLocation::createConditionalColonLoc( @@ -148,7 +253,7 @@ return true; } -/// \brief Determines whether two expression trees are identical regarding +/// \brief Determines whether two statement trees are identical regarding /// operators and symbols. /// /// Exceptions: expressions containing macros or functions with possible side @@ -156,40 +261,51 @@ /// Limitations: (t + u) and (u + t) are not considered identical. /// t*(u + t) and t*u + t*t are not considered identical. /// -static bool isIdenticalExpr(const ASTContext &Ctx, const Expr *Expr1, - const Expr *Expr2, bool IgnoreSideEffects) { - // If Expr1 & Expr2 are of different class then they are not - // identical expression. - if (Expr1->getStmtClass() != Expr2->getStmtClass()) +static bool isIdenticalStmt(const ASTContext &Ctx, const Stmt *Stmt1, + const Stmt *Stmt2, bool IgnoreSideEffects) { + + if (!Stmt1 || !Stmt2) { + if (!Stmt1 && !Stmt2) + return true; return false; - // If Expr1 has side effects then don't warn even if expressions - // are identical. - if (!IgnoreSideEffects && Expr1->HasSideEffects(Ctx)) + } + + // If Stmt1 & Stmt2 are of different class then they are not + // identical statements. + if (Stmt1->getStmtClass() != Stmt2->getStmtClass()) return false; - // If either expression comes from a macro then don't warn even if - // the expressions are identical. - if ((Expr1->getExprLoc().isMacroID()) || (Expr2->getExprLoc().isMacroID())) - return false; - // If all children of two expressions are identical, return true. - Expr::const_child_iterator I1 = Expr1->child_begin(); - Expr::const_child_iterator I2 = Expr2->child_begin(); - while (I1 != Expr1->child_end() && I2 != Expr2->child_end()) { - const Expr *Child1 = dyn_cast<Expr>(*I1); - const Expr *Child2 = dyn_cast<Expr>(*I2); - if (!Child1 || !Child2 || !isIdenticalExpr(Ctx, Child1, Child2, - IgnoreSideEffects)) + + const Expr *Expr1 = dyn_cast<Expr>(Stmt1); + const Expr *Expr2 = dyn_cast<Expr>(Stmt2); + + if (Expr1 && Expr2) { + // If Stmt1 has side effects then don't warn even if expressions + // are identical. + if (!IgnoreSideEffects && Expr1->HasSideEffects(Ctx)) return false; - ++I1; - ++I2; + // If either expression comes from a macro then don't warn even if + // the expressions are identical. + if ((Expr1->getExprLoc().isMacroID()) || (Expr2->getExprLoc().isMacroID())) + return false; + + // If all children of two expressions are identical, return true. + Expr::const_child_iterator I1 = Expr1->child_begin(); + Expr::const_child_iterator I2 = Expr2->child_begin(); + while (I1 != Expr1->child_end() && I2 != Expr2->child_end()) { + if (!*I1 || !*I2 || !isIdenticalStmt(Ctx, *I1, *I2, IgnoreSideEffects)) + return false; + ++I1; + ++I2; + } + // If there are different number of children in the statements, return + // false. + if (I1 != Stmt1->child_end()) + return false; + if (I2 != Stmt2->child_end()) + return false; } - // If there are different number of children in the expressions, return false. - // (TODO: check if this is a redundant condition.) - if (I1 != Expr1->child_end()) - return false; - if (I2 != Expr2->child_end()) - return false; - switch (Expr1->getStmtClass()) { + switch (Stmt1->getStmtClass()) { default: return false; case Stmt::CallExprClass: @@ -197,40 +313,125 @@ case Stmt::CStyleCastExprClass: case Stmt::ImplicitCastExprClass: case Stmt::ParenExprClass: + case Stmt::BreakStmtClass: + case Stmt::ContinueStmtClass: + case Stmt::NullStmtClass: return true; + case Stmt::ReturnStmtClass: { + const ReturnStmt *ReturnStmt1 = cast<ReturnStmt>(Stmt1); + const ReturnStmt *ReturnStmt2 = cast<ReturnStmt>(Stmt2); + + return isIdenticalStmt(Ctx, ReturnStmt1->getRetValue(), + ReturnStmt2->getRetValue(), IgnoreSideEffects); + } + case Stmt::ForStmtClass: { + const ForStmt *ForStmt1 = cast<ForStmt>(Stmt1); + const ForStmt *ForStmt2 = cast<ForStmt>(Stmt2); + + if (!isIdenticalStmt(Ctx, ForStmt1->getInit(), ForStmt2->getInit(), + IgnoreSideEffects)) + return false; + if (!isIdenticalStmt(Ctx, ForStmt1->getCond(), ForStmt2->getCond(), + IgnoreSideEffects)) + return false; + if (!isIdenticalStmt(Ctx, ForStmt1->getInc(), ForStmt2->getInc(), + IgnoreSideEffects)) + return false; + if (!isIdenticalStmt(Ctx, ForStmt1->getBody(), ForStmt2->getBody(), + IgnoreSideEffects)) + return false; + return true; + } + case Stmt::DoStmtClass: { + const DoStmt *DStmt1 = cast<DoStmt>(Stmt1); + const DoStmt *DStmt2 = cast<DoStmt>(Stmt2); + + if (!isIdenticalStmt(Ctx, DStmt1->getCond(), DStmt2->getCond(), + IgnoreSideEffects)) + return false; + if (!isIdenticalStmt(Ctx, DStmt1->getBody(), DStmt2->getBody(), + IgnoreSideEffects)) + return false; + return true; + } + case Stmt::WhileStmtClass: { + const WhileStmt *WStmt1 = cast<WhileStmt>(Stmt1); + const WhileStmt *WStmt2 = cast<WhileStmt>(Stmt2); + + return isIdenticalStmt(Ctx, WStmt1->getCond(), WStmt2->getCond(), + IgnoreSideEffects); + } + case Stmt::IfStmtClass: { + const IfStmt *IStmt1 = cast<IfStmt>(Stmt1); + const IfStmt *IStmt2 = cast<IfStmt>(Stmt2); + + if (!isIdenticalStmt(Ctx, IStmt1->getCond(), IStmt2->getCond(), + IgnoreSideEffects)) + return false; + if (!isIdenticalStmt(Ctx, IStmt1->getThen(), IStmt2->getThen(), + IgnoreSideEffects)) + return false; + if (!isIdenticalStmt(Ctx, IStmt1->getElse(), IStmt2->getElse(), + IgnoreSideEffects)) + return false; + return true; + } + case Stmt::CompoundStmtClass: { + const CompoundStmt *CompStmt1 = cast<CompoundStmt>(Stmt1); + const CompoundStmt *CompStmt2 = cast<CompoundStmt>(Stmt2); + + if (CompStmt1->size() != CompStmt2->size()) + return false; + + CompoundStmt::const_body_iterator I1 = CompStmt1->body_begin(); + CompoundStmt::const_body_iterator I2 = CompStmt2->body_begin(); + while (I1 != CompStmt1->body_end() && I2 != CompStmt2->body_end()) { + if (!isIdenticalStmt(Ctx, *I1, *I2, IgnoreSideEffects)) + return false; + ++I1; + ++I2; + } + + return true; + } case Stmt::BinaryOperatorClass: { - const BinaryOperator *BinOp1 = cast<BinaryOperator>(Expr1); - const BinaryOperator *BinOp2 = cast<BinaryOperator>(Expr2); + const BinaryOperator *BinOp1 = cast<BinaryOperator>(Stmt1); + const BinaryOperator *BinOp2 = cast<BinaryOperator>(Stmt2); return BinOp1->getOpcode() == BinOp2->getOpcode(); } case Stmt::CharacterLiteralClass: { - const CharacterLiteral *CharLit1 = cast<CharacterLiteral>(Expr1); - const CharacterLiteral *CharLit2 = cast<CharacterLiteral>(Expr2); + const CharacterLiteral *CharLit1 = cast<CharacterLiteral>(Stmt1); + const CharacterLiteral *CharLit2 = cast<CharacterLiteral>(Stmt2); return CharLit1->getValue() == CharLit2->getValue(); } case Stmt::DeclRefExprClass: { - const DeclRefExpr *DeclRef1 = cast<DeclRefExpr>(Expr1); - const DeclRefExpr *DeclRef2 = cast<DeclRefExpr>(Expr2); + const DeclRefExpr *DeclRef1 = cast<DeclRefExpr>(Stmt1); + const DeclRefExpr *DeclRef2 = cast<DeclRefExpr>(Stmt2); return DeclRef1->getDecl() == DeclRef2->getDecl(); } case Stmt::IntegerLiteralClass: { - const IntegerLiteral *IntLit1 = cast<IntegerLiteral>(Expr1); - const IntegerLiteral *IntLit2 = cast<IntegerLiteral>(Expr2); + const IntegerLiteral *IntLit1 = cast<IntegerLiteral>(Stmt1); + const IntegerLiteral *IntLit2 = cast<IntegerLiteral>(Stmt2); return IntLit1->getValue() == IntLit2->getValue(); } case Stmt::FloatingLiteralClass: { - const FloatingLiteral *FloatLit1 = cast<FloatingLiteral>(Expr1); - const FloatingLiteral *FloatLit2 = cast<FloatingLiteral>(Expr2); + const FloatingLiteral *FloatLit1 = cast<FloatingLiteral>(Stmt1); + const FloatingLiteral *FloatLit2 = cast<FloatingLiteral>(Stmt2); return FloatLit1->getValue().bitwiseIsEqual(FloatLit2->getValue()); } + case Stmt::StringLiteralClass: { + const StringLiteral *StringLit1 = cast<StringLiteral>(Stmt1); + const StringLiteral *StringLit2 = cast<StringLiteral>(Stmt2); + return StringLit1->getString() == StringLit2->getString(); + } case Stmt::MemberExprClass: { - const MemberExpr *MemberExpr1 = cast<MemberExpr>(Expr1); - const MemberExpr *MemberExpr2 = cast<MemberExpr>(Expr2); - return MemberExpr1->getMemberDecl() == MemberExpr2->getMemberDecl(); + const MemberExpr *MemberStmt1 = cast<MemberExpr>(Stmt1); + const MemberExpr *MemberStmt2 = cast<MemberExpr>(Stmt2); + return MemberStmt1->getMemberDecl() == MemberStmt2->getMemberDecl(); } case Stmt::UnaryOperatorClass: { - const UnaryOperator *UnaryOp1 = cast<UnaryOperator>(Expr1); - const UnaryOperator *UnaryOp2 = cast<UnaryOperator>(Expr2); + const UnaryOperator *UnaryOp1 = cast<UnaryOperator>(Stmt1); + const UnaryOperator *UnaryOp2 = cast<UnaryOperator>(Stmt2); return UnaryOp1->getOpcode() == UnaryOp2->getOpcode(); } } Index: test/Analysis/misc-ps-region-store.cpp =================================================================== --- test/Analysis/misc-ps-region-store.cpp (revision 200459) +++ test/Analysis/misc-ps-region-store.cpp (working copy) @@ -588,6 +588,7 @@ int x = int(); if (!x) { clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} + ++x; // Suppress warning that both branches are identical } else { clang_analyzer_warnIfReached(); // no-warning Index: test/Analysis/identical-expressions.cpp =================================================================== --- test/Analysis/identical-expressions.cpp (revision 200459) +++ test/Analysis/identical-expressions.cpp (working copy) @@ -1158,3 +1158,118 @@ int c = 3; a = a > 5 ? (b > 5 ? 1 : 4) : (b > 5 ? 4 : 4); // expected-warning {{identical expressions on both sides of ':' in conditional expression}} } + +void test_identical_branches1(bool b) { + int i = 0; + if (b) { // expected-warning {{true and false branches are identical}} + ++i; + } else { + ++i; + } +} + +void test_identical_branches2(bool b) { + int i = 0; + if (b) { // expected-warning {{true and false branches are identical}} + ++i; + } else + ++i; +} + +void test_identical_branches3(bool b) { + int i = 0; + if (b) { // no warning + ++i; + } else { + i++; + } +} + +void test_identical_branches4(bool b) { + int i; + if (b) { // expected-warning {{true and false branches are identical}} + for (i = 0; i < 4; ++i) + ; + } else { + for (i = 0; i < 4; ++i) + ; + } +} + +void test_identical_branches5(bool b) { + while (true) { + if (b) // expected-warning {{true and false branches are identical}} + break; + else + break; + } +} + +void test_identical_branches6(bool b) { + if (b) // expected-warning {{true and false branches are identical}} + func(); + else + func(); +} + +void test_identical_branches7(bool b) { + if (b) // no-warning + funcParam(1); + else + funcParam(2); +} + +void test_identical_bitwise1() { + int a = 5 | 5; // expected-warning {{identical expressions on both sides of bitwise operator}} +} + +void test_identical_bitwise2() { + int a = 5; + int b = a | a; // expected-warning {{identical expressions on both sides of bitwise operator}} +} + +void test_identical_bitwise3() { + int a = 5; + int b = (a | a); // expected-warning {{identical expressions on both sides of bitwise operator}} +} + +void test_identical_bitwise4() { + int a = 4; + int b = a | 4; // no-warning +} + +void test_identical_bitwise5() { + int a = 4; + int b = 4; + int c = a | b; // no-warning +} + +void test_identical_bitwise6() { + int a = 5; + int b = a | 4 | a; // expected-warning {{identical expressions on both sides of bitwise operator}} +} + +void test_identical_bitwise7() { + int a = 5; + int b = func() | func(); // no-warning +} + +void test_identical_logical1(int a) { + if (a == 4 && a == 4) // expected-warning {{identical expressions on both sides of logical operator}} + ; +} + +void test_identical_logical2(int a) { + if (a == 4 || a == 5 || a == 4) // expected-warning {{identical expressions on both sides of logical operator}} + ; +} + +void test_identical_logical3(int a) { + if (a == 4 || a == 5 || a == 6) // no-warning + ; +} + +void test_identical_logical4(int a) { + if (a == func() || a == func()) // no-warning + ; +}
> _______________________________________________ > cfe-commits mailing list > cfe-commits@cs.uiuc.edu > http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
_______________________________________________ cfe-commits mailing list cfe-commits@cs.uiuc.edu http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits