Hi, Jordan. On Thu, 2014-01-30 at 09:47 -0800, Jordan Rose wrote: > 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.
No problem. I have fixed your comments with the two attached patches. if_stmt adds support to test if both branches are identical and should be applied first. bin_op adds support to find identical expressions when using binary operators. > + // 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 Thanks. I thought about a better way to fix this when I implemented it, but couldn't find a good one. First, somehow, sorting the expressions and check only adjacent ones will probably be slower in practice due to the overhead of sorting. > 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. Test cases added. > 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. I ran some tests with php 5.5.8 and openssl 1.0.1f. The tests were done on my laptop with a ssd and 8GB of ram, so the numbers should be cpu bound. Between each run I completely removed the directory and reconfigured to make sure conditions were as similar as possible. The checker was modified t only check the branches of an if statement, not the binary operators. Adding the check when running scan-build on php increased the time by 0.2%. On openssl it actually decreased by 0.15% instead. I think the background tasks of my otherwise idle desktop has more influence on the numbers than the test. php 5.5.8 base run: real 53m44.366s user 51m31.245s sys 1m11.288s php 5.5.8 with checking if stmts: real 53m51.311s user 51m35.985s sys 1m12.949s openssl 1.0.1f base run: real 11m40.819s user 10m39.124s sys 0m49.091s openssl 1.0.1f with checking if stmts: real 11m39.783s user 10m38.536s sys 0m48.879s Best regards, Daniel Fahlgren
diff --git a/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp b/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp index faa8cd5..ad9b6cc 100644 --- a/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp @@ -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 *Stmt1, + const Stmt *Stmt2, bool IgnoreSideEffects = false); //===----------------------------------------------------------------------===// // FindIdenticalExprVisitor - Identify nodes using identical expressions. //===----------------------------------------------------------------------===// @@ -39,8 +39,10 @@ public: 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: @@ -49,6 +51,39 @@ private: }; } // end anonymous namespace +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 (const CompoundStmt *CompStmt = dyn_cast<CompoundStmt>(Stmt1)) { + if (CompStmt->size() == 1) + Stmt1 = CompStmt->body_back(); + } + if (const CompoundStmt *CompStmt = dyn_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 branches", + 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)) @@ -104,7 +139,7 @@ bool FindIdenticalExprVisitor::VisitBinaryOperator(const BinaryOperator *B) { // 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; @@ -127,7 +162,7 @@ bool FindIdenticalExprVisitor::VisitConditionalOperator( // 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 +183,7 @@ bool FindIdenticalExprVisitor::VisitConditionalOperator( 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,81 +191,183 @@ bool FindIdenticalExprVisitor::VisitConditionalOperator( /// 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()) - return false; - // If Expr1 has side effects then don't warn even if expressions - // are identical. - if (!IgnoreSideEffects && Expr1->HasSideEffects(Ctx)) +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 either expression comes from a macro then don't warn even if - // the expressions are identical. - if ((Expr1->getExprLoc().isMacroID()) || (Expr2->getExprLoc().isMacroID())) + } + + // If Stmt1 & Stmt2 are of different class then they are not + // identical statements. + if (Stmt1->getStmtClass() != Stmt2->getStmtClass()) 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; + // 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 != Expr1->child_end()) + return false; + if (I2 != Expr2->child_end()) return false; - ++I1; - ++I2; } - // 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: case Stmt::ArraySubscriptExprClass: - case Stmt::CStyleCastExprClass: case Stmt::ImplicitCastExprClass: case Stmt::ParenExprClass: + case Stmt::BreakStmtClass: + case Stmt::ContinueStmtClass: + case Stmt::NullStmtClass: + return true; + case Stmt::CStyleCastExprClass: { + const CStyleCastExpr* CastExpr1 = cast<CStyleCastExpr>(Stmt1); + const CStyleCastExpr* CastExpr2 = cast<CStyleCastExpr>(Stmt2); + + return CastExpr1->getTypeAsWritten() == CastExpr2->getTypeAsWritten(); + } + 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::CompoundAssignOperatorClass: 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(); } } diff --git a/test/Analysis/identical-expressions.cpp b/test/Analysis/identical-expressions.cpp index 3a331fb..70bd12c 100644 --- a/test/Analysis/identical-expressions.cpp +++ b/test/Analysis/identical-expressions.cpp @@ -1043,6 +1043,11 @@ void test_float() { a = a > 5 ? a : a; // expected-warning {{identical expressions on both sides of ':' in conditional expression}} } +const char *test_string() { + float a = 0; + return a > 5 ? "abc" : "abc"; // expected-warning {{identical expressions on both sides of ':' in conditional expression}} +} + void test_unsigned_expr() { unsigned a = 0; unsigned b = 0; @@ -1158,3 +1163,142 @@ void test_signed_nested_cond_expr() { 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_branches_break(bool b) { + while (true) { + if (b) // expected-warning {{true and false branches are identical}} + break; + else + break; + } +} + +void test_identical_branches_continue(bool b) { + while (true) { + if (b) // expected-warning {{true and false branches are identical}} + continue; + else + continue; + } +} + +void test_identical_branches_func(bool b) { + if (b) // expected-warning {{true and false branches are identical}} + func(); + else + func(); +} + +void test_identical_branches_func_arguments(bool b) { + if (b) // no-warning + funcParam(1); + else + funcParam(2); +} + +void test_identical_branches_cast1(bool b) { + long v = -7; + if (b) // no-warning + v = (signed int) v; + else + v = (unsigned int) v; +} + +void test_identical_branches_cast2(bool b) { + long v = -7; + if (b) // expected-warning {{true and false branches are identical}} + v = (signed int) v; + else + v = (signed int) v; +} + +int test_identical_branches_return_int(bool b) { + int i = 0; + if (b) { // expected-warning {{true and false branches are identical}} + i++; + return i; + } else { + i++; + return i; + } +} + +int test_identical_branches_return_func(bool b) { + if (b) { // expected-warning {{true and false branches are identical}} + return func(); + } else { + return func(); + } +} + +void test_identical_branches_for(bool b) { + int i; + int j; + if (b) { // expected-warning {{true and false branches are identical}} + for (i = 0, j = 0; i < 10; i++) + j += 4; + } else { + for (i = 0, j = 0; i < 10; i++) + j += 4; + } +} + +void test_identical_branches_while(bool b) { + int i = 10; + if (b) { // expected-warning {{true and false branches are identical}} + while (func()) + i--; + } else { + while (func()) + i--; + } +} + +void test_identical_branches_do_while(bool b) { + int i = 10; + if (b) { // expected-warning {{true and false branches are identical}} + do { + i--; + } while (func()); + } else { + do { + i--; + } while (func()); + } +} + +void test_identical_branches_if(bool b, int i) { + if (b) { // expected-warning {{true and false branches are identical}} + if (i < 5) + i += 10; + } else { + if (i < 5) + i += 10; + } +} diff --git a/test/Analysis/misc-ps-region-store.cpp b/test/Analysis/misc-ps-region-store.cpp index 6a873f0..388a769 100644 --- a/test/Analysis/misc-ps-region-store.cpp +++ b/test/Analysis/misc-ps-region-store.cpp @@ -588,6 +588,7 @@ void rdar11401827() { 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
diff --git a/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp b/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp index ad9b6cc..22d3d37 100644 --- a/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp @@ -48,9 +48,59 @@ public: private: BugReporter &BR; AnalysisDeclContext *AC; + + void reportIdenticalExpr(const BinaryOperator *B, bool CheckBitwise, + ArrayRef<SourceRange> Sr); + void checkBitwiseOrLogicalOp(const BinaryOperator *B, bool CheckBitwise); + void checkComparisionOp(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(), + "Use 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 (const BinaryOperator *B2 = dyn_cast<BinaryOperator>(LHS)) { + if (B->getOpcode() != B2->getOpcode()) + 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(); @@ -86,8 +136,25 @@ bool FindIdenticalExprVisitor::VisitIfStmt(const IfStmt *I) { 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)) + checkComparisionOp(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::checkComparisionOp(const BinaryOperator *B) { + BinaryOperator::Opcode Op = B->getOpcode(); + // // Special case for floating-point representation. // @@ -120,21 +187,21 @@ bool FindIdenticalExprVisitor::VisitBinaryOperator(const BinaryOperator *B) { (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. } @@ -150,10 +217,6 @@ bool FindIdenticalExprVisitor::VisitBinaryOperator(const BinaryOperator *B) { 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( diff --git a/test/Analysis/identical-expressions.cpp b/test/Analysis/identical-expressions.cpp index 70bd12c..ad16bac 100644 --- a/test/Analysis/identical-expressions.cpp +++ b/test/Analysis/identical-expressions.cpp @@ -1302,3 +1302,74 @@ void test_identical_branches_if(bool b, int i) { i += 10; } } + +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 + ; +} + +void test_identical_logical5(int x, int y) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wlogical-op-parentheses" + if (x == 4 && y == 5 || x == 4 && y == 6) // no-warning + ; +#pragma clang diagnostic pop +} + +void test_identical_logical6(int x, int y) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wlogical-op-parentheses" + if (x == 4 && y == 5 || x == 4 && y == 5) // expected-warning {{identical expressions on both sides of logical operator}} + ; +#pragma clang diagnostic pop +}
_______________________________________________ cfe-commits mailing list cfe-commits@cs.uiuc.edu http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits