/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checkcondition.h" #include "errortypes.h" #include "library.h" #include "platform.h" #include "preprocessor.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" #include #include #include #include #include #include #include class TestCondition : public TestFixture { public: TestCondition() : TestFixture("TestCondition") {} private: Settings settings0; Settings settings1; void run() override { // known platform.. settings0.platform(cppcheck::Platform::PlatformType::Native); settings1.platform(cppcheck::Platform::PlatformType::Native); LOAD_LIB_2(settings0.library, "qt.cfg"); LOAD_LIB_2(settings0.library, "std.cfg"); settings0.severity.enable(Severity::style); settings0.severity.enable(Severity::warning); const char cfg[] = "\n" "\n" " \n" ""; tinyxml2::XMLDocument xmldoc; xmldoc.Parse(cfg, sizeof(cfg)); settings1.severity.enable(Severity::style); settings1.severity.enable(Severity::warning); settings1.library.load(xmldoc); TEST_CASE(assignAndCompare); // assignment and comparison don't match TEST_CASE(mismatchingBitAnd); // overlapping bitmasks TEST_CASE(comparison); // CheckCondition::comparison test cases TEST_CASE(multicompare); // mismatching comparisons TEST_CASE(overlappingElseIfCondition); // overlapping conditions in if and else-if TEST_CASE(oppositeElseIfCondition); // opposite conditions in if and else-if TEST_CASE(checkBadBitmaskCheck); TEST_CASE(incorrectLogicOperator1); TEST_CASE(incorrectLogicOperator2); TEST_CASE(incorrectLogicOperator3); TEST_CASE(incorrectLogicOperator4); TEST_CASE(incorrectLogicOperator5); // complex expressions TEST_CASE(incorrectLogicOperator6); // char literals TEST_CASE(incorrectLogicOperator7); // opposite expressions: (expr || !expr) TEST_CASE(incorrectLogicOperator8); // ! TEST_CASE(incorrectLogicOperator9); TEST_CASE(incorrectLogicOperator10); // enum TEST_CASE(incorrectLogicOperator11); TEST_CASE(incorrectLogicOperator12); TEST_CASE(incorrectLogicOperator13); TEST_CASE(incorrectLogicOperator14); TEST_CASE(incorrectLogicOperator15); TEST_CASE(incorrectLogicOperator16); // #10070 TEST_CASE(secondAlwaysTrueFalseWhenFirstTrueError); TEST_CASE(incorrectLogicOp_condSwapping); TEST_CASE(testBug5895); TEST_CASE(testBug5309); TEST_CASE(modulo); TEST_CASE(oppositeInnerCondition); TEST_CASE(oppositeInnerConditionPointers); TEST_CASE(oppositeInnerConditionClass); TEST_CASE(oppositeInnerConditionUndeclaredVariable); TEST_CASE(oppositeInnerConditionAlias); TEST_CASE(oppositeInnerCondition2); TEST_CASE(oppositeInnerCondition3); TEST_CASE(oppositeInnerConditionAnd); TEST_CASE(oppositeInnerConditionEmpty); TEST_CASE(oppositeInnerConditionFollowVar); TEST_CASE(identicalInnerCondition); TEST_CASE(identicalConditionAfterEarlyExit); TEST_CASE(innerConditionModified); TEST_CASE(clarifyCondition1); // if (a = b() < 0) TEST_CASE(clarifyCondition2); // if (a & b == c) TEST_CASE(clarifyCondition3); // if (! a & b) TEST_CASE(clarifyCondition4); // ticket #3110 TEST_CASE(clarifyCondition5); // #3609 CWinTraits.. TEST_CASE(clarifyCondition6); // #3818 TEST_CASE(clarifyCondition7); TEST_CASE(clarifyCondition8); TEST_CASE(alwaysTrue); TEST_CASE(alwaysTrueSymbolic); TEST_CASE(alwaysTrueInfer); TEST_CASE(alwaysTrueContainer); TEST_CASE(alwaysTrueLoop); TEST_CASE(alwaysTrueTryCatch); TEST_CASE(multiConditionAlwaysTrue); TEST_CASE(duplicateCondition); TEST_CASE(checkInvalidTestForOverflow); TEST_CASE(checkConditionIsAlwaysTrueOrFalseInsideIfWhile); TEST_CASE(alwaysTrueFalseInLogicalOperators); TEST_CASE(pointerAdditionResultNotNull); TEST_CASE(duplicateConditionalAssign); TEST_CASE(checkAssignmentInCondition); TEST_CASE(compareOutOfTypeRange); TEST_CASE(knownConditionCast); // #9976 TEST_CASE(knownConditionIncrementLoop); // #9808 } void check(const char code[], Settings *settings, const char* filename = "test.cpp") { // Clear the error buffer.. errout.str(""); // Raw tokens.. std::vector files(1, filename); std::istringstream istr(code); const simplecpp::TokenList tokens1(istr, files, files[0]); // Preprocess.. simplecpp::TokenList tokens2(files); std::map filedata; simplecpp::preprocess(tokens2, tokens1, files, filedata, simplecpp::DUI()); Preprocessor preprocessor(*settings, nullptr); preprocessor.setDirectives(tokens1); // Tokenizer.. Tokenizer tokenizer(settings, this); tokenizer.createTokens(std::move(tokens2)); tokenizer.simplifyTokens1(""); tokenizer.setPreprocessor(&preprocessor); // Run checks.. CheckCondition checkCondition; checkCondition.runChecks(&tokenizer, settings, this); } void check(const char code[], const char* filename = "test.cpp", bool inconclusive = false) { settings0.certainty.setEnabled(Certainty::inconclusive, inconclusive); check(code, &settings0, filename); } void assignAndCompare() { // & check("void foo(int x)\n" "{\n" " int y = x & 4;\n" " if (y == 3);\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Mismatching assignment and comparison, comparison 'y==3' is always false.\n", errout.str()); check("void foo(int x)\n" "{\n" " int y = x & 4;\n" " if (y != 3);\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Mismatching assignment and comparison, comparison 'y!=3' is always true.\n", errout.str()); // | check("void foo(int x) {\n" " int y = x | 0x14;\n" " if (y == 0x710);\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Mismatching assignment and comparison, comparison 'y==0x710' is always false.\n", errout.str()); check("void foo(int x) {\n" " int y = x | 0x14;\n" " if (y == 0x71f);\n" "}"); ASSERT_EQUALS("", errout.str()); // various simple assignments check("void foo(int x) {\n" " int y = (x+1) | 1;\n" " if (y == 2);\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Mismatching assignment and comparison, comparison 'y==2' is always false.\n", errout.str()); check("void foo() {\n" " int y = 1 | x();\n" " if (y == 2);\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Mismatching assignment and comparison, comparison 'y==2' is always false.\n", errout.str()); // multiple conditions check("void foo(int x) {\n" " int y = x & 4;\n" " if ((y == 3) && (z == 1));\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Mismatching assignment and comparison, comparison 'y==3' is always false.\n", errout.str()); check("void foo(int x) {\n" " int y = x & 4;\n" " if ((x==123) || ((y == 3) && (z == 1)));\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Mismatching assignment and comparison, comparison 'y==3' is always false.\n", errout.str()); check("void f(int x) {\n" " int y = x & 7;\n" " if (setvalue(&y) && y != 8);\n" "}"); ASSERT_EQUALS("", errout.str()); // recursive checking into scopes check("void f(int x) {\n" " int y = x & 7;\n" " if (z) y=0;\n" " else { if (y==8); }\n" // always false "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (style) Mismatching assignment and comparison, comparison 'y==8' is always false.\n", errout.str()); // while check("void f(int x) {\n" " int y = x & 7;\n" " while (y==8);\n" // local variable => always false "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Mismatching assignment and comparison, comparison 'y==8' is always false.\n", errout.str()); check("void f(int x) {\n" " extern int y; y = x & 7;\n" " while (y==8);\n" // non-local variable => no error "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " int a = 100;\n" " while (x) {\n" " int y = 16 | a;\n" " while (y != 0) y--;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void g(int x);\n" "void f(int x) {\n" " int a = 100;\n" " while (x) {\n" " int y = 16 | a;\n" " while (y != 0) g(y);\n" " }\n" "}"); ASSERT_EQUALS( "[test.cpp:5] -> [test.cpp:6]: (style) Mismatching assignment and comparison, comparison 'y!=0' is always true.\n", errout.str()); check("void g(int &x);\n" "void f(int x) {\n" " int a = 100;\n" " while (x) {\n" " int y = 16 | a;\n" " while (y != 0) g(y);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // calling function check("void f(int x) {\n" " int y = x & 7;\n" " do_something();\n" " if (y==8);\n" // local variable => always false "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (style) Mismatching assignment and comparison, comparison 'y==8' is always false.\n", errout.str()); check("void f(int x) {\n" " int y = x & 7;\n" " do_something(&y);\n" // passing variable => no error " if (y==8);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void do_something(int);\n" "void f(int x) {\n" " int y = x & 7;\n" " do_something(y);\n" " if (y==8);\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (style) Mismatching assignment and comparison, comparison 'y==8' is always false.\n", errout.str()); check("void f(int x) {\n" " extern int y; y = x & 7;\n" " do_something();\n" " if (y==8);\n" // non-local variable => no error "}"); ASSERT_EQUALS("", errout.str()); // #4434 : false positive: ?: check("void f(int x) {\n" " x = x & 1;\n" " x = x & 1 ? 1 : -1;\n" " if(x != -1) { }\n" "}"); ASSERT_EQUALS("", errout.str()); // #4735 check("void f() {\n" " int x = *(char*)&0x12345678;\n" " if (x==18) { }\n" "}"); ASSERT_EQUALS("", errout.str()); // bailout: no variable info check("void foo(int x) {\n" " y = 2 | x;\n" // y not declared => no error " if(y == 1) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // bailout: negative number check("void foo(int x) {\n" " int y = -2 | x;\n" // negative number => no error " if (y==1) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // bailout: pass variable to function check("void foo(int x) {\n" " int y = 2 | x;\n" " bar(&y);\n" // pass variable to function => no error " if (y==1) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // no crash on unary operator& (#5643) check("SdrObject* ApplyGraphicToObject() {\n" " if (&rHitObject) {}\n" " else if (rHitObject.IsClosedObj() && !&rHitObject) { }\n" "}"); ASSERT_EQUALS("", errout.str()); // #5695: increment check("void f(int a0, int n) {\n" " int c = a0 & 3;\n" " for (int a = 0; a < n; a++) {\n" " c++;\n" " if (c == 4)\n" " c = 0;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int a) {\n" // #6662 " int x = a & 1;\n" " while (x <= 4) {\n" " if (x != 5) {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (style) Mismatching assignment and comparison, comparison 'x!=5' is always true.\n", errout.str()); check("void f(int a) {\n" // #6662 " int x = a & 1;\n" " while ((x += 4) < 10) {\n" " if (x != 5) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int x = 100;\n" " while (x) {\n" " g(x);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void g(int x);\n" "void f() {\n" " int x = 100;\n" " while (x) {\n" " g(x);\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Condition 'x' is always true\n", errout.str()); check("void g(int & x);\n" "void f() {\n" " int x = 100;\n" " while (x) {\n" " g(x);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void mismatchingBitAnd() { check("void f(int a) {\n" " int b = a & 0xf0;\n" " b &= 1;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Mismatching bitmasks. Result is always 0 (X = Y & 0xf0; Z = X & 0x1; => Z=0).\n", errout.str()); check("void f(int a) {\n" " int b = a & 0xf0;\n" " int c = b & 1;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Mismatching bitmasks. Result is always 0 (X = Y & 0xf0; Z = X & 0x1; => Z=0).\n", errout.str()); check("void f(int a) {\n" " int b = a;" " switch (x) {\n" " case 1: b &= 1; break;\n" " case 2: b &= 2; break;\n" " };\n" "}"); ASSERT_EQUALS("", errout.str()); } void comparison() { // CheckCondition::comparison test cases // '==' check("void f(int a) {\n assert( (a & 0x07) == 8U );\n}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X & 0x7) == 0x8' is always false.\n",errout.str()); check("void f(int a) {\n assert( (a & b & 4 & c ) == 3 );\n}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X & 0x4) == 0x3' is always false.\n", errout.str()); check("void f(int a) {\n assert( (a | 0x07) == 8U );\n}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X | 0x7) == 0x8' is always false.\n",errout.str()); check("void f(int a) {\n assert( (a & 0x07) == 7U );\n}"); ASSERT_EQUALS("", errout.str()); check("void f(int a) {\n assert( (a | 0x01) == -15 );\n}"); ASSERT_EQUALS("", errout.str()); // '!=' check("void f(int a) {\n assert( (a & 0x07) != 8U );\n}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X & 0x7) != 0x8' is always true.\n",errout.str()); check("void f(int a) {\n assert( (a | 0x07) != 8U );\n}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X | 0x7) != 0x8' is always true.\n",errout.str()); check("void f(int a) {\n assert( (a & 0x07) != 7U );\n}"); ASSERT_EQUALS("", errout.str()); check("void f(int a) {\n assert( (a | 0x07) != 7U );\n}"); ASSERT_EQUALS("", errout.str()); // '>=' check("void f(int a) {\n assert( (a & 0x07) >= 8U );\n}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X & 0x7) >= 0x8' is always false.\n",errout.str()); check("void f(unsigned int a) {\n assert( (a | 0x7) >= 7U );\n}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X | 0x7) >= 0x7' is always true.\n",errout.str()); check("void f(int a) {\n assert( (a & 0x07) >= 7U );\n}"); ASSERT_EQUALS("",errout.str()); check("void f(int a) {\n assert( (a | 0x07) >= 8U );\n}"); ASSERT_EQUALS("",errout.str()); //correct for negative 'a' // '>' check("void f(int a) {\n assert( (a & 0x07) > 7U );\n}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X & 0x7) > 0x7' is always false.\n",errout.str()); check("void f(unsigned int a) {\n assert( (a | 0x7) > 6U );\n}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X | 0x7) > 0x6' is always true.\n",errout.str()); check("void f(int a) {\n assert( (a & 0x07) > 6U );\n}"); ASSERT_EQUALS("",errout.str()); check("void f(int a) {\n assert( (a | 0x07) > 7U );\n}"); ASSERT_EQUALS("",errout.str()); //correct for negative 'a' // '<=' check("void f(int a) {\n assert( (a & 0x07) <= 7U );\n}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X & 0x7) <= 0x7' is always true.\n",errout.str()); check("void f(unsigned int a) {\n assert( (a | 0x08) <= 7U );\n}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X | 0x8) <= 0x7' is always false.\n",errout.str()); check("void f(int a) {\n assert( (a & 0x07) <= 6U );\n}"); ASSERT_EQUALS("",errout.str()); check("void f(int a) {\n assert( (a | 0x08) <= 7U );\n}"); ASSERT_EQUALS("",errout.str()); //correct for negative 'a' // '<' check("void f(int a) {\n assert( (a & 0x07) < 8U );\n}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X & 0x7) < 0x8' is always true.\n",errout.str()); check("void f(unsigned int a) {\n assert( (a | 0x07) < 7U );\n}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X | 0x7) < 0x7' is always false.\n",errout.str()); check("void f(int a) {\n assert( (a & 0x07) < 3U );\n}"); ASSERT_EQUALS("",errout.str()); check("void f(int a) {\n assert( (a | 0x07) < 7U );\n}"); ASSERT_EQUALS("",errout.str()); //correct for negative 'a' } #define checkPureFunction(code) checkPureFunction_(code, __FILE__, __LINE__) void multicompare() { check("void foo(int x)\n" "{\n" " if (x & 7);\n" " else { if (x == 1); }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Expression is always false because 'else if' condition matches previous condition at line 3.\n", errout.str()); check("void foo(int x)\n" "{\n" " if (x & 7);\n" " else { if (x & 1); }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Expression is always false because 'else if' condition matches previous condition at line 3.\n", errout.str()); check("extern int bar() __attribute__((pure));\n" "void foo(int x)\n" "{\n" " if ( bar() >1 && b) {}\n" " else if (bar() >1 && b) {}\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Expression is always false because 'else if' condition matches previous condition at line 4.\n", errout.str()); checkPureFunction("extern int bar();\n" "void foo(int x)\n" "{\n" " if ( bar() >1 && b) {}\n" " else if (bar() >1 && b) {}\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Expression is always false because 'else if' condition matches previous condition at line 4.\n", errout.str()); // 7284 check("void foo() {\n" " if (a) {}\n" " else if (!!a) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout.str()); // #11059 check("int f();\n" "void g() {\n" " int i = f();\n" " if (i == 3) {}\n" " else if ((i = f()) == 5) {}\n" " else if (i == 3) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("int f();\n" "void g() {\n" " int i = f();\n" " if (i == 3) {}\n" " else if ((i = f()) == 5) {}\n" " else if (i != 3) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void checkPureFunction_(const char code[], const char* file, int line) { // Clear the error buffer.. errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings1, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); CheckCondition checkCondition; checkCondition.runChecks(&tokenizer, &settings1, this); } void overlappingElseIfCondition() { check("void f(int a, int &b) {\n" " if (a) { b = 1; }\n" " else { if (a) { b = 2; } }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout.str()); check("void f(int a, int &b) {\n" " if (a) { b = 1; }\n" " else { if (a) { b = 2; } }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout.str()); check("void f(int a, int &b) {\n" " if (a == 1) { b = 1; }\n" " else { if (a == 2) { b = 2; }\n" " else { if (a == 1) { b = 3; } } }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout.str()); check("void f(int a, int &b) {\n" " if (a == 1) { b = 1; }\n" " else { if (a == 2) { b = 2; }\n" " else { if (a == 2) { b = 3; } } }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Expression is always false because 'else if' condition matches previous condition at line 3.\n", errout.str()); check("void f(int a, int &b) {\n" " if (a++) { b = 1; }\n" " else { if (a++) { b = 2; }\n" " else { if (a++) { b = 3; } } }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int a, int &b) {\n" " if (!strtok(NULL, \" \")) { b = 1; }\n" " else { if (!strtok(NULL, \" \")) { b = 2; } }\n" "}"); ASSERT_EQUALS("", errout.str()); { check("void f(Class &c) {\n" " if (c.dostuff() == 3) {}\n" " else { if (c.dostuff() == 3) {} }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(const Class &c) {\n" " if (c.dostuff() == 3) {}\n" " else { if (c.dostuff() == 3) {} }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout.str()); } check("void f(int a, int &b) {\n" " x = x / 2;\n" " if (x < 100) { b = 1; }\n" " else { x = x / 2; if (x < 100) { b = 2; } }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int i) {\n" " if(i == 0x02e2000000 || i == 0xa0c6000000)\n" " foo(i);\n" "}"); ASSERT_EQUALS("", errout.str()); // ticket 3689 ( avoid false positive ) check("int fitInt(long long int nValue){\n" " if( nValue < 0x7fffffffLL )\n" " {\n" " return 32;\n" " }\n" " if( nValue < 0x7fffffffffffLL )\n" " {\n" " return 48;\n" " }\n" " else {\n" " if( nValue < 0x7fffffffffffffffLL )\n" " {\n" " return 64;\n" " } else\n" " {\n" " return -1;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(WIDGET *widget) {\n" " if (dynamic_cast(widget)){}\n" " else if (dynamic_cast(widget)){}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" // #6482 " if (x & 1) {}\n" " else if (x == 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if (x & 15) {}\n" " else if (x == 40) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout.str()); check("void f(int x) {\n" " if (x == sizeof(double)) {}\n" " else { if (x == sizeof(long double)) {} }" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if (x & 0x08) {}\n" " else if (x & 0xF8) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if (x & 0xF8) {}\n" " else if (x & 0x08) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout.str()); check("void f(bool a, bool b) {\n" " if(a && b){}\n" " else if( !!b && !!a){}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout.str()); check("void f(bool a, bool b) {\n" " if(a && b){}\n" " else if( !!b && a){}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout.str()); check("void f(bool a, bool b) {\n" " if(a && b){}\n" " else if( b && !!a){}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout.str()); check("void f(bool a, bool b) {\n" " if(a && b){}\n" " else if( b && !(!a)){}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout.str()); check("void f(bool a, bool b) {\n" " if(a && b){}\n" " else if( !!b && !(!a)){}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout.str()); check("void f(bool a, bool b) {\n" " if(a && b){}\n" " else if( !!(b) && !!(a+b)){}\n" "}"); ASSERT_EQUALS("", errout.str()); // #8168 check("enum MaskValues\n" "{\n" " Value1 = 0x00000001,\n" " Value2 = 0x00000002\n" "};\n" "void TestFunction(int value) {\n" " if ( value & (int)Value1 ) {}\n" " else if ( value & (int)Value2 ) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(size_t x) {\n" " if (x == sizeof(int)) {}\n" " else { if (x == sizeof(long))} {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f(size_t x) {\n" " if (x == sizeof(long)) {}\n" " else { if (x == sizeof(long long))} {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void oppositeElseIfCondition() { setMultiline(); check("void f(int x) {\n" " if (x) {}\n" " else if (!x) {}\n" "}"); ASSERT_EQUALS("test.cpp:3:style:Expression is always true because 'else if' condition is opposite to previous condition at line 2.\n" "test.cpp:2:note:first condition\n" "test.cpp:3:note:else if condition is opposite to first condition\n", errout.str()); check("void f(int x) {\n" " int y = x;\n" " if (x) {}\n" " else if (!y) {}\n" "}"); ASSERT_EQUALS("test.cpp:4:style:Expression is always true because 'else if' condition is opposite to previous condition at line 3.\n" "test.cpp:2:note:'y' is assigned value 'x' here.\n" "test.cpp:3:note:first condition\n" "test.cpp:4:note:else if condition is opposite to first condition\n", errout.str()); } void checkBadBitmaskCheck() { check("bool f(int x) {\n" " bool b = x | 0x02;\n" " return b;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?\n", errout.str()); check("bool f(int x) {\n" " bool b = 0x02 | x;\n" " return b;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?\n", errout.str()); check("int f(int x) {\n" " int b = x | 0x02;\n" " return b;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f(int x) {\n" " bool b = x & 0x02;\n" " return b;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f(int x) {\n" " if(x | 0x02)\n" " return b;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?\n", errout.str()); check("bool f(int x) {\n" " int y = 0x1;\n" " if(b) y = 0;\n" " if(x | y)\n" " return b;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f(int x) {\n" " foo(a && (x | 0x02));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?\n", errout.str()); check("int f(int x) {\n" " return (x | 0x02) ? 0 : 5;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?\n", errout.str()); check("int f(int x) {\n" " return x ? (x | 0x02) : 5;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f(int x) {\n" " return x | 0x02;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?\n", errout.str()); check("bool f(int x) {\n" " if (x) {\n" " return x | 0x02;\n" " }\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?\n", errout.str()); check("const bool f(int x) {\n" " return x | 0x02;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?\n", errout.str()); check("struct F {\n" " static const bool f(int x) {\n" " return x | 0x02;\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (warning) Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?\n", errout.str()); check("struct F {\n" " typedef bool b_t;\n" "};\n" "F::b_t f(int x) {\n" " return x | 0x02;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (warning) Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?\n", errout.str()); check("int f(int x) {\n" " return x | 0x02;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void create_rop_masks_4( rop_mask_bits *bits) {\n" "DWORD mask_offset;\n" "BYTE *and_bits = bits->and;\n" "rop_mask *rop_mask;\n" "and_bits[mask_offset] |= (rop_mask->and & 0x0f);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(unsigned a, unsigned b) {\n" " unsigned cmd1 = b & 0x0F;\n" " if (cmd1 | a) {\n" " if (b == 0x0C) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int i) {\n" // #11082 " int j = 0;\n" " if (i | j) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (warning) Operator '|' with one operand equal to zero is redundant.\n", errout.str()); check("#define EIGHTTOIS(x) (((x) << 8) | (x))\n" "int f() {\n" " return EIGHTTOIS(0);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("#define O_RDONLY 0\n" "void f(const char* s, int* pFd) {\n" " *pFd = open(s, O_RDONLY | O_BINARY, 0);\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void incorrectLogicOperator1() { check("void f(int x) {\n" " if ((x != 1) || (x != 3))\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x != 1 || x != 3.\n", errout.str()); check("void f(int x) {\n" " if (1 != x || 3 != x)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x != 1 || x != 3.\n", errout.str()); check("void f(int x) {\n" " if (x<0 && !x) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 0 && !x.\n", errout.str()); check("void f(int x) {\n" " if (x==0 && x) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x == 0 && x.\n", errout.str()); check("void f(int x) {\n" // ast.. " if (y == 1 && x == 1 && x == 7) { }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x == 1 && x == 7.\n", errout.str()); check("void f(int x, int y) {\n" " if (x != 1 || y != 1)\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x, int y) {\n" " if ((y == 1) && (x != 1) || (x != 3))\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x, int y) {\n" " if ((x != 1) || (x != 3) && (y == 1))\n" " a++;\n" "}" ); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (style) Condition 'x!=3' is always true\n", errout.str()); check("void f(int x) {\n" " if ((x != 1) && (x != 3))\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if ((x == 1) || (x == 3))\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x, int y) {\n" " if ((x != 1) || (y != 3))\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x, int y) {\n" " if ((x != hotdog) || (y != hotdog))\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x, int y) {\n" " if ((x != 5) || (y != 5))\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if ((x != 5) || (x != 6))\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x != 5 || x != 6.\n", errout.str()); check("void f(unsigned int a, unsigned int b, unsigned int c) {\n" " if((a != b) || (c != b) || (c != a))\n" " {\n" " return true;\n" " }\n" " return false;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (style) Condition 'c!=a' is always false\n", errout.str()); } void incorrectLogicOperator2() { check("void f(float x) {\n" " if ((x == 1) && (x == 1.0))\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if ((x == 1) && (x == 0x00000001))\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (style) Condition 'x==0x00000001' is always true\n", errout.str()); check("void f(int x) {\n" " if (x == 1 && x == 3)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x == 1 && x == 3.\n", errout.str()); check("void f(int x) {\n" " if (x == 1.0 && x == 3.0)\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); // float comparisons with == and != are not checked right now - such comparison is a bad idea check("void f(float x) {\n" " if (x == 1 && x == 1.0)\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void bar(float f) {\n" // #5246 " if ((f > 0) && (f < 1)) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if (x < 1 && x > 1)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 1 && x > 1.\n", errout.str()); check("void f(int x) {\n" " if (x < 1.0 && x > 1.0)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 1.0 && x > 1.0.\n", errout.str()); check("void f(int x) {\n" " if (x < 1 && x > 1.0)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 1 && x > 1.0.\n", errout.str()); check("void f(int x) {\n" " if (x >= 1.0 && x <= 1.001)\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if (x < 1 && x > 3)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 1 && x > 3.\n", errout.str()); check("void f(float x) {\n" " if (x < 1.0 && x > 3.0)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 1.0 && x > 3.0.\n", errout.str()); check("void f(int x) {\n" " if (1 > x && 3 < x)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 1 && x > 3.\n", errout.str()); check("void f(int x) {\n" " if (x < 3 && x > 1)\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if (x > 3 || x < 10)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x > 3 || x < 10.\n", errout.str()); check("void f(int x) {\n" " if (x >= 3 || x <= 10)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x >= 3 || x <= 10.\n", errout.str()); check("void f(int x) {\n" " if (x >= 3 || x < 10)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x >= 3 || x < 10.\n", errout.str()); check("void f(int x) {\n" " if (x > 3 || x <= 10)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x > 3 || x <= 10.\n", errout.str()); check("void f(int x) {\n" " if (x > 3 || x < 3)\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if (x >= 3 || x <= 3)\n" " a++;\n" "}" ); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x >= 3 || x <= 3.\n", errout.str()); check("void f(int x) {\n" " if (x >= 3 || x < 3)\n" " a++;\n" "}" ); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x >= 3 || x < 3.\n", errout.str()); check("void f(int x) {\n" " if (x > 3 || x <= 3)\n" " a++;\n" "}" ); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x > 3 || x <= 3.\n", errout.str()); check("void f(int x) {\n" " if((x==3) && (x!=4))\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: If 'x == 3', the comparison 'x != 4' is always true.\n", errout.str()); check("void f(const std::string &s) {\n" // #8860 " const std::size_t p = s.find(\"42\");\n" " const std::size_t * const ptr = &p;\n" " if(p != std::string::npos && p == 0 && *ptr != 1){;}\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:4]: (style) Condition '*ptr!=1' is always true\n", errout.str()); check("void f(int x) {\n" " if ((x!=4) && (x==3))\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: If 'x == 3', the comparison 'x != 4' is always true.\n", errout.str()); check("void f(int x) {\n" " if ((x==3) || (x!=4))\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: If 'x == 3', the comparison 'x != 4' is always true.\n", errout.str()); check("void f(int x) {\n" " if ((x!=4) || (x==3))\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: If 'x == 3', the comparison 'x != 4' is always true.\n", errout.str()); check("void f(int x) {\n" " if ((x==3) && (x!=3))\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x == 3 && x != 3.\n", errout.str()); check("void f(int x) {\n" " if ((x==6) || (x!=6))\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x == 6 || x != 6.\n", errout.str()); check("void f(int x) {\n" " if (x > 10 || x < 3)\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if (x > 5 && x == 1)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x > 5 && x == 1.\n", errout.str()); check("void f(int x) {\n" " if (x > 5 && x == 6)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: If 'x == 6', the comparison 'x > 5' is always true.\n", errout.str()); // #3419 check("void f() {\n" " if ( &q != &a && &q != &b ) { }\n" "}"); ASSERT_EQUALS("", errout.str()); // #3676 check("void f(int m_x2, int w, int x) {\n" " if (x + w - 1 > m_x2 || m_x2 < 0 )\n" " m_x2 = x + w - 1;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(float x) {\n" // x+1 => x " if (x <= 1.0e20 && x >= -1.0e20) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(float x) {\n" // x+1 => x " if (x >= 1.0e20 && x <= 1.0e21) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(float x) {\n" // x+1 => x " if (x <= -1.0e20 && x >= -1.0e21) {}\n" "}"); ASSERT_EQUALS("", errout.str()); } void incorrectLogicOperator3() { check("void f(int x, bool& b) {\n" " b = x > 5 && x == 1;\n" " c = x < 1 && x == 3;\n" " d = x >= 5 && x == 1;\n" " e = x <= 1 && x == 3;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x > 5 && x == 1.\n" "[test.cpp:3]: (warning) Logical conjunction always evaluates to false: x < 1 && x == 3.\n" "[test.cpp:4]: (warning) Logical conjunction always evaluates to false: x >= 5 && x == 1.\n" "[test.cpp:5]: (warning) Logical conjunction always evaluates to false: x <= 1 && x == 3.\n", errout.str()); } void incorrectLogicOperator4() { check("#define ZERO 0\n" "void f(int x) {\n" " if (x && x != ZERO) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int N) {\n" // #9789 " T a[20] = { 0 };\n" " for (int i = 0; i < N; ++i) {\n" " if (0 < a[i] && a[i] < 1) {}\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void incorrectLogicOperator5() { // complex expressions check("void f(int x) {\n" " if (x+3 > 2 || x+3 < 10) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: EXPR > 2 || EXPR < 10.\n", errout.str()); } void incorrectLogicOperator6() { // char literals check("void f(char x) {\n" " if (x == '1' || x == '2') {}\n" "}", "test.cpp", true); ASSERT_EQUALS("", errout.str()); check("void f(char x) {\n" " if (x == '1' && x == '2') {}\n" "}", "test.cpp", true); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x == '1' && x == '2'.\n", errout.str()); check("int f(char c) {\n" " return (c >= 'a' && c <= 'z');\n" "}", "test.cpp", true); ASSERT_EQUALS("", errout.str()); check("int f(char c) {\n" " return (c <= 'a' && c >= 'z');\n" "}", "test.cpp", true); ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Logical conjunction always evaluates to false: c <= 'a' && c >= 'z'.\n", errout.str()); check("int f(char c) {\n" " return (c <= 'a' && c >= 'z');\n" "}", "test.cpp", false); ASSERT_EQUALS("", errout.str()); } void incorrectLogicOperator7() { // opposite expressions check("void f(int i) {\n" " if (i || !i) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: i || !(i).\n", errout.str()); check("void f(int a, int b) {\n" " if (a>b || a<=b) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: a > b || a <= b.\n", errout.str()); check("void f(int a, int b) {\n" " if (a>b || a T icdf( const T uniform ) {\n" " if ((0 -1.0 - 1.0e-12))\n" " return;\n" " else\n" " return;\n" "}"); ASSERT_EQUALS("", errout.str()); } void incorrectLogicOperator8() { // opposite expressions check("void f(int i) {\n" " if (!(i!=10) && !(i!=20)) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: !(i != 10) && !(i != 20).\n", errout.str()); } void incorrectLogicOperator9() { // #6069 "False positive incorrectLogicOperator due to dynamic_cast" check("class MyType;\n" "class OtherType;\n" "void foo (OtherType* obj) {\n" " assert((!obj) || dynamic_cast(obj));\n" "}"); ASSERT_EQUALS("", errout.str()); } void incorrectLogicOperator10() { // #7794 - enum check("typedef enum { A, B } Type_t;\n" "void f(Type_t t) {\n" " if ((t == A) && (t == B))\n" " {}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Logical conjunction always evaluates to false: t == 0 && t == 1.\n", errout.str()); } void incorrectLogicOperator11() { check("void foo(int i, const int n) { if ( i < n && i == n ) {} }"); ASSERT_EQUALS("[test.cpp:1]: (warning) Logical conjunction always evaluates to false: i < n && i == n.\n", errout.str()); check("void foo(int i, const int n) { if ( i > n && i == n ) {} }"); ASSERT_EQUALS("[test.cpp:1]: (warning) Logical conjunction always evaluates to false: i > n && i == n.\n", errout.str()); check("void foo(int i, const int n) { if ( i == n && i > n ) {} }"); ASSERT_EQUALS("[test.cpp:1]: (warning) Logical conjunction always evaluates to false: i == n && i > n.\n", errout.str()); check("void foo(int i, const int n) { if ( i == n && i < n ) {} }"); ASSERT_EQUALS("[test.cpp:1]: (warning) Logical conjunction always evaluates to false: i == n && i < n.\n", errout.str()); } void incorrectLogicOperator12() { // #8696 check("struct A {\n" " void f() const;\n" "};\n" "void foo(A a) {\n" " A x = a;\n" " A y = a;\n" " y.f();\n" " if (a > x && a < y)\n" " return;\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:6] -> [test.cpp:8]: (warning) Logical conjunction always evaluates to false: a > x && a < y.\n", errout.str()); check("struct A {\n" " void f();\n" "};\n" "void foo(A a) {\n" " A x = a;\n" " A y = a;\n" " y.f();\n" " if (a > x && a < y)\n" " return;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(A a) {\n" " A x = a;\n" " A y = a;\n" " y.f();\n" " if (a > x && a < y)\n" " return;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(A a) {\n" " const A x = a;\n" " const A y = a;\n" " y.f();\n" " if (a > x && a < y)\n" " return;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3] -> [test.cpp:5]: (warning) Logical conjunction always evaluates to false: a > x && a < y.\n", errout.str()); } void incorrectLogicOperator13() { // 8780 check("void f(const int &v) {\n" " const int x=v;\n" " if ((v == 1) && (x == 2)) {;}\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Logical conjunction always evaluates to false: v == 1 && x == 2.\n", errout.str()); check("void f2(const int *v) {\n" " const int *x=v;\n" " if ((*v == 1) && (*x == 2)) {;}\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Logical conjunction always evaluates to false: *(v) == 1 && *(x) == 2.\n", errout.str()); } void incorrectLogicOperator14() { check("static const std ::string h;\n" "class i {\n" "public:\n" " struct j {\n" " std ::string k;\n" " std ::string l;\n" " };\n" " struct a {\n" " enum { m = 1 };\n" " };\n" "} b;\n" "namespace n {\n" "class c;\n" "}\n" "struct o {\n" " enum { p, d, q, r };\n" " enum { e, f };\n" "\n" "public:\n" " class j {\n" " public:\n" " class s {\n" " std ::string a;\n" " };\n" " };\n" "};\n" "namespace n {\n" "class b;\n" "}\n" "namespace aa {\n" "class d {\n" "public:\n" " char t;\n" " enum {} u;\n" "};\n" "} // namespace aa\n" "namespace aa {\n" "struct e {};\n" "} // namespace aa\n" "class a;\n" "class w {\n" "public:\n" " enum { x };\n" " struct {\n" " } y;\n" " std ::string z;\n" "};\n" "class ab {\n" " friend class c;\n" "\n" "public:\n" " class ac {\n" " void e(const ac &v) const;\n" " };\n" "};\n" "class f;\n" "class ad {\n" " friend class e;\n" " enum { e, ae, ag, ah, ai, aj, ak, a, b };\n" " class c {};\n" " class d {\n" " enum am { f, an, ao, ap, aq, ar, b, as, at, c, au };\n" " enum av { aw, ax, ay, az, e, ba, bb, bc, bd, a };\n" " struct b {\n" " am action;\n" " av c;\n" " };\n" " };\n" " class e {\n" " public:\n" " std ::string e;\n" " class f {\n" " } f;\n" " class be {\n" " public:\n" " };\n" " std ::vector bf;\n" " enum { bg, b } c;\n" " };\n" " struct bh {\n" " std ::map b;\n" " };\n" " std ::map bi;\n" " struct {\n" " int b;\n" " char bj;\n" " } bk;\n" " class a {\n" " public:\n" " std ::set b;\n" " };\n" "};\n" "class bl;\n" "class al;\n" "class bm;\n" "class f;\n" "class b;\n" "class bn;\n" "namespace bo {\n" "class bp {\n" "public:\n" " typedef std ::pair bq;\n" " typedef std ::list br;\n" "};\n" "const bo ::bp *dg(const f *a, const al *b);\n" "} // namespace bo\n" "const bn *dh(const f *d, bo ::bp ::br &bs);\n" "class f {\n" "public:\n" " struct bt {};\n" " std ::vector f;\n" "};\n" "class bu;\n" "class a;\n" "class c;\n" "struct bv {};\n" "class af {\n" "private:\n" "public:\n" " enum { b, d, e, f, c, bw };\n" " void a(int c);\n" " af *bx() const;\n" "};\n" "namespace by {\n" "class b;\n" "}\n" "class b {\n" "public:\n" " bool d, c;\n" "};\n" "class bz;\n" "class f;\n" "class ca {\n" " friend class b;\n" "\n" "public:\n" " const bm *cb() const { return cc; }\n" " f *d(f *e, bool f) const;\n" " int e() { return ++cd; }\n" " bl *const c;\n" " bm *cc;\n" " std ::map ce;\n" " int cd;\n" " bz *a;\n" "};\n" "namespace n {\n" "class c;\n" "class d;\n" "} // namespace n\n" "class cf {\n" "public:\n" " explicit cf(const std ::string &aname);\n" " cf(const std ::string &aname, const ca *cg, const al *ch, bl *ci)\n" " : cj(cg), ck(ch), cl(ci), cn(aname) {}\n" "\n" "protected:\n" " const ca *const cj;\n" " const al *const ck;\n" " bl *const cl;\n" " const std ::string cn;\n" "};\n" "class cm : public cf {\n" "public:\n" " void cp();\n" " std ::string d() const;\n" "};\n" "struct co {\n" " co();\n" " const bu *a;\n" " enum f {};\n" " enum {\n" " b = (1 << 0),\n" " c = (1 << 1),\n" " };\n" " void d(bool e);\n" "};\n" "class bu {\n" " friend class e;\n" "\n" "public:\n" " struct f {};\n" " enum { d, cr, cq, ct, cs, e, a, b, c, dd, cu, cv, cw, cx, cy, cz, da };\n" " const f *db;\n" " const af *dc;\n" "} f{};\n" "class bm {\n" "public:\n" " std ::list df;\n" " std ::vector de;\n" " mutable std ::set f;\n" "};\n" "void cm ::cp() {\n" " const bm *a = cj->cb();\n" " for (const bu *b : a->de)\n" " for (af *c = b->dc->bx();;) {\n" " af *d = c;\n" " af *e = c;\n" " bool f(d);\n" " bool g(e);\n" " if (f && g)\n" " ;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:200] -> [test.cpp:200]: (style) Condition 'g' is always true\n", errout.str()); } void incorrectLogicOperator15() { // 10022 check("struct PipeRoute {\n" " std::deque points;\n" " std::deque estimates;\n" "};\n" "void CleanPipeRoutes(std::map& pipeRoutes) {\n" " for (auto it = pipeRoutes.begin(); it != pipeRoutes.end(); ) {\n" " PipeRoute* curRoute = it->second;\n" " if (curRoute->points.empty() && curRoute->estimates.size() != 2)\n" " {\n" " delete curRoute;\n" " it = pipeRoutes.erase(it);\n" " }\n" " else\n" " {\n" " ++it;\n" " }\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void incorrectLogicOperator16() { // #10070 check("void foo(void* p) {\n" " if (!p || p == -1) { }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void secondAlwaysTrueFalseWhenFirstTrueError() { check("void f(void) {\n" // #8892 " const char c[1] = { \'x\' }; \n" " if(c[0] == \'x\'){;}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'c[0]=='x'' is always true\n", errout.str()); check("void f(int x) {\n" " if (x > 5 && x != 1)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: If 'x > 5', the comparison 'x != 1' is always true.\n", errout.str()); check("void f(int x) {\n" " if (x > 5 && x != 6)\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if ((x > 5) && (x != 1))\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: If 'x > 5', the comparison 'x != 1' is always true.\n", errout.str()); check("void f(int x) {\n" " if ((x > 5) && (x != 6))\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x, bool& b) {\n" " b = x > 3 || x == 4;\n" " c = x < 5 || x == 4;\n" " d = x >= 3 || x == 4;\n" " e = x <= 5 || x == 4;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: If 'x == 4', the comparison 'x > 3' is always true.\n" "[test.cpp:3]: (style) Redundant condition: If 'x == 4', the comparison 'x < 5' is always true.\n" "[test.cpp:4]: (style) Redundant condition: If 'x == 4', the comparison 'x >= 3' is always true.\n" "[test.cpp:5]: (style) Redundant condition: If 'x == 4', the comparison 'x <= 5' is always true.\n", errout.str()); check("void f(int x, bool& b) {\n" " b = x > 5 || x != 1;\n" " c = x < 1 || x != 3;\n" " d = x >= 5 || x != 1;\n" " e = x <= 1 || x != 3;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: If 'x > 5', the comparison 'x != 1' is always true.\n" "[test.cpp:3]: (style) Redundant condition: If 'x < 1', the comparison 'x != 3' is always true.\n" "[test.cpp:4]: (style) Redundant condition: If 'x >= 5', the comparison 'x != 1' is always true.\n" "[test.cpp:5]: (style) Redundant condition: If 'x <= 1', the comparison 'x != 3' is always true.\n", errout.str()); check("void f(int x, bool& b) {\n" " b = x > 6 && x > 5;\n" " c = x > 5 || x > 6;\n" " d = x < 6 && x < 5;\n" " e = x < 5 || x < 6;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: If 'x > 6', the comparison 'x > 5' is always true.\n" "[test.cpp:3]: (style) Redundant condition: If 'x > 6', the comparison 'x > 5' is always true.\n" "[test.cpp:4]: (style) Redundant condition: If 'x < 5', the comparison 'x < 6' is always true.\n" "[test.cpp:5]: (style) Redundant condition: If 'x < 5', the comparison 'x < 6' is always true.\n", errout.str()); } void incorrectLogicOp_condSwapping() { check("void f(int x) {\n" " if (x < 1 && x > 3)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 1 && x > 3.\n", errout.str()); check("void f(int x) {\n" " if (1 > x && x > 3)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 1 && x > 3.\n", errout.str()); check("void f(int x) {\n" " if (x < 1 && 3 < x)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 1 && x > 3.\n", errout.str()); check("void f(int x) {\n" " if (1 > x && 3 < x)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 1 && x > 3.\n", errout.str()); check("void f(int x) {\n" " if (x > 3 && x < 1)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x > 3 && x < 1.\n", errout.str()); check("void f(int x) {\n" " if (3 < x && x < 1)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x > 3 && x < 1.\n", errout.str()); check("void f(int x) {\n" " if (x > 3 && 1 > x)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x > 3 && x < 1.\n", errout.str()); check("void f(int x) {\n" " if (3 < x && 1 > x)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x > 3 && x < 1.\n", errout.str()); } void modulo() { check("bool f(bool& b1, bool& b2, bool& b3) {\n" " b1 = a % 5 == 4;\n" " b2 = a % c == 100000;\n" " b3 = a % 5 == c;\n" " return a % 5 == 5-p;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f(bool& b1, bool& b2, bool& b3, bool& b4, bool& b5) {\n" " b1 = a % 5 < 5;\n" " b2 = a % 5 <= 5;\n" " b3 = a % 5 == 5;\n" " b4 = a % 5 != 5;\n" " b5 = a % 5 >= 5;\n" " return a % 5 > 5;\n" "}"); ASSERT_EQUALS( "[test.cpp:7]: (style) Condition 'a%5>5' is always false\n" "[test.cpp:2]: (warning) Comparison of modulo result is predetermined, because it is always less than 5.\n" "[test.cpp:3]: (warning) Comparison of modulo result is predetermined, because it is always less than 5.\n" "[test.cpp:4]: (warning) Comparison of modulo result is predetermined, because it is always less than 5.\n" "[test.cpp:5]: (warning) Comparison of modulo result is predetermined, because it is always less than 5.\n" "[test.cpp:6]: (warning) Comparison of modulo result is predetermined, because it is always less than 5.\n" "[test.cpp:7]: (warning) Comparison of modulo result is predetermined, because it is always less than 5.\n", errout.str()); check("void f(bool& b1, bool& b2) {\n" " b1 = bar() % 5 < 889;\n" " if(x[593] % 5 <= 5)\n" " b2 = x.a % 5 == 5;\n" "}"); ASSERT_EQUALS( "[test.cpp:3]: (style) Condition 'x[593]%5<=5' is always true\n" "[test.cpp:2]: (warning) Comparison of modulo result is predetermined, because it is always less than 5.\n" "[test.cpp:3]: (warning) Comparison of modulo result is predetermined, because it is always less than 5.\n" "[test.cpp:4]: (warning) Comparison of modulo result is predetermined, because it is always less than 5.\n", errout.str()); check("void f() {\n" " if (a % 2 + b % 2 == 2)\n" " foo();\n" "}"); ASSERT_EQUALS("", errout.str()); } void oppositeInnerCondition() { check("void foo(int a, int b) {\n" " if(a==b)\n" " if(a!=b)\n" " cout << a;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("bool foo(int a, int b) {\n" " if(a==b)\n" " return a!=b;\n" " return false;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Opposite inner 'return' condition leads to a dead code block.\n", errout.str()); check("void foo(int a, int b) {\n" " if(a==b)\n" " if(b!=a)\n" " cout << a;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void foo(int a) {\n" " if(a >= 50) {\n" " if(a < 50)\n" " cout << a;\n" " else\n" " cout << 100;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); // #4186 check("void foo(int a) {\n" " if(a >= 50) {\n" " if(a > 50)\n" " cout << a;\n" " else\n" " cout << 100;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // 4170 check("class foo {\n" " void bar() {\n" " if (tok == '(') {\n" " next();\n" " if (tok == ',') {\n" " next();\n" " if (tok != ',') {\n" " op->reg2 = asm_parse_reg();\n" " }\n" " skip(',');\n" " }\n" " }\n" " }\n" " void next();\n" " const char *tok;\n" "};"); ASSERT_EQUALS("", errout.str()); check("void foo(int i)\n" "{\n" " if(i > 5) {\n" " i = bar();\n" " if(i < 5) {\n" " cout << a;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int& i) {\n" " i=6;\n" "}\n" "void bar(int i) {\n" " if(i>5) {\n" " foo(i);\n" " if(i<5) {\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int& i);\n" "void bar() {\n" " int i; i = func();\n" " if(i>5) {\n" " foo(i);\n" " if(i<5) {\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int i);\n" "void bar(int i) {\n" " if(i>5) {\n" " foo(i);\n" " if(i<5) {\n" " }\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void foo(const int &i);\n" "void bar(int i) {\n" " if(i>5) {\n" " foo(i);\n" " if(i<5) {\n" " }\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void foo(int i);\n" "void bar() {\n" " int i; i = func();\n" " if(i>5) {\n" " foo(i);\n" " if(i<5) {\n" " }\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:6]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("class C { void f(int &i) const; };\n" // #7028 - variable is changed by const method "void foo(C c, int i) {\n" " if (i==5) {\n" " c.f(i);\n" " if (i != 5) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // see linux revision 1f80c0cc check("int generic_write_sync(int,int,int);\n" "\n" "void cifs_writev(int i) {\n" " int rc = __generic_file_aio_write();\n" " if (rc > 0){\n" " err = generic_write_sync(file, iocb->ki_pos - rc, rc);\n" " if(rc < 0) {\n" // <- condition is always false " err = rc;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:7]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); // #5874 - array check("void testOppositeConditions2() {\n" " int array[2] = { 0, 0 };\n" " if (array[0] < 2) {\n" " array[0] += 5;\n" " if (array[0] > 2) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #6227 - FP caused by simplifications of casts and known variables check("void foo(A *a) {\n" " if(a) {\n" " B *b = dynamic_cast(a);\n" " if(!b) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int a) {\n" " if(a) {\n" " int b = a;\n" " if(!b) {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void foo(unsigned u) {\n" " if (u != 0) {\n" " for (int i=0; i<32; i++) {\n" " if (u == 0) {}\n" // <- don't warn " u = x;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #8186 check("void f() {\n" " for (int i=0;i<4;i++) {\n" " if (i==5) {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); // #8938 check("void Delete(SS_CELLCOORD upperleft) {\n" " if ((upperleft.Col == -1) && (upperleft.Row == -1)) {\n" " GetActiveCell(&(upperleft.Col), &(upperleft.Row));\n" " if (upperleft.Row == -1) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #9702 check("struct A {\n" " void DoTest() {\n" " if (!IsSet()) {\n" " m_value = true;\n" " if (IsSet());\n" " }\n" " }\n" " bool IsSet() const { return m_value; }\n" " bool m_value = false;\n" "};"); ASSERT_EQUALS("", errout.str()); } void oppositeInnerConditionPointers() { check("void f(struct ABC *abc) {\n" " struct AB *ab = abc->ab;\n" " if (ab->a == 123){\n" " do_something(abc);\n" // might change ab->a " if (ab->a != 123) {\n" " err = rc;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void Fred::f() {\n" // daca: ace " if (this->next_ == map_man_->table_) {\n" " this->next_ = n;\n" " if (this->next_ != map_man_->table_) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void test(float *f) {\n" // #7405 " if(*f>10) {\n" " (*f) += 0.1f;\n" " if(*f<10) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("int * f(int * x, int * y) {\n" " if(!x) return x;\n" " return y;\n" "}"); ASSERT_EQUALS("", errout.str()); } void oppositeInnerConditionClass() { // #6095 - calling member function that might change the state check("void f() {\n" " const Fred fred;\n" // <- fred is const, warn " if (fred.isValid()) {\n" " fred.dostuff();\n" " if (!fred.isValid()) {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("class Fred { public: bool isValid() const; void dostuff() const; };\n" "void f() {\n" " Fred fred;\n" " if (fred.isValid()) {\n" " fred.dostuff();\n" // <- dostuff() is const, warn " if (!fred.isValid()) {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:6]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f() {\n" " Fred fred;\n" " if (fred.isValid()) {\n" " fred.dostuff();\n" " if (!fred.isValid()) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #6385 "crash in Variable::getFlag()" check("class TranslationHandler {\n" "QTranslator *mTranslator;\n" "void SetLanguage() {\n" " if (mTranslator) {\n" " qApp->removeTranslator(mTranslator);\n" " }\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); // just don't crash... check("bool f(std::ofstream &CFileStream) {\n" // #8198 " if(!CFileStream.good()) { return; }\n" " CFileStream << \"abc\";\n" " if (!CFileStream.good()) {}\n" "}"); ASSERT_EQUALS("", errout.str()); } void oppositeInnerConditionUndeclaredVariable() { // #5731 - fp when undeclared variable is used check("void f() {\n" " if (x == -1){\n" " x = do_something();\n" " if (x != -1) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #5750 - another fp when undeclared variable is used check("void f() {\n" " if (r < w){\n" " r += 3;\n" " if (r > w) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #6574 - another fp when undeclared variable is used check("void foo() {\n" " if(i) {\n" " i++;\n" " if(!i) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // undeclared array check("void f(int x) {\n" " if (a[x] > 0) {\n" " a[x] -= dt;\n" " if (a[x] < 0) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #6313 - false positive: opposite conditions in nested if blocks when condition changed check("void Foo::Bar() {\n" " if(var){\n" " --var;\n" " if(!var){}\n" " else {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // daca hyphy check("bool f() {\n" " if (rec.lLength==0) {\n" " rec.Delete(i);\n" " if (rec.lLength!=0) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void oppositeInnerConditionAlias() { check("void f() {\n" " struct S s;\n" " bool hasFailed = false;\n" " s.status = &hasFailed;\n" "\n" " if (! hasFailed) {\n" " doStuff(&s);\n" " if (hasFailed) {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (style) Condition '!hasFailed' is always true\n", errout.str()); } void oppositeInnerCondition2() { // first comparison: < check("void f(int x) {\n" "\n" " if (x<4) {\n" " if (x==5) {}\n" // <- Warning " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f(int x) {\n" "\n" " if (x<4) {\n" " if (x!=5) {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Condition 'x!=5' is always true\n", errout.str()); check("void f(int x) {\n" "\n" " if (x<4) {\n" " if (x>5) {}\n" // <- Warning " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f(int x) {\n" "\n" " if (x<4) {\n" " if (x>=5) {}\n" // <- Warning " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f(int x) {\n" "\n" " if (x<4) {\n" " if (x<5) {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Condition 'x<5' is always true\n", errout.str()); check("void f(int x) {\n" "\n" " if (x<4) {\n" " if (x<=5) {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Condition 'x<=5' is always true\n", errout.str()); check("void f(int x) {\n" "\n" " if (x<5) {\n" " if (x==4) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" "\n" " if (x<5) {\n" " if (x!=4) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" "\n" " if (x<5) {\n" " if (x!=6) {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Condition 'x!=6' is always true\n", errout.str()); check("void f(int x) {\n" "\n" " if (x<5) {\n" " if (x>4) {}\n" // <- Warning " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Condition 'x>4' is always false\n", errout.str()); check("void f(int x) {\n" "\n" " if (x<5) {\n" " if (x>=4) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" "\n" " if (x<5) {\n" " if (x<4) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" "\n" " if (x<5) {\n" " if (x<=4) {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Condition 'x<=4' is always true\n", errout.str()); // first comparison: > check("void f(int x) {\n" "\n" " if (x>4) {\n" " if (x==5) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" "\n" " if (x>4) {\n" " if (x>5) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" "\n" " if (x>4) {\n" " if (x>=5) {}\n" // <- Warning " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Condition 'x>=5' is always true\n", errout.str()); check("void f(int x) {\n" "\n" " if (x>4) {\n" " if (x<5) {}\n" // <- Warning " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Condition 'x<5' is always false\n", errout.str()); check("void f(int x) {\n" "\n" " if (x>4) {\n" " if (x<=5) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" "\n" " if (x>5) {\n" " if (x==4) {}\n" // <- Warning " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f(int x) {\n" "\n" " if (x>5) {\n" " if (x>4) {}\n" // <- Warning " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Condition 'x>4' is always true\n", errout.str()); check("void f(int x) {\n" "\n" " if (x>5) {\n" " if (x>=4) {}\n" // <- Warning " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Condition 'x>=4' is always true\n", errout.str()); check("void f(int x) {\n" "\n" " if (x>5) {\n" " if (x<4) {}\n" // <- Warning " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f(int x) {\n" "\n" " if (x>5) {\n" " if (x<=4) {}\n" // <- Warning " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f(int x) {\n" " if (x < 4) {\n" " if (10 < x) {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); } void oppositeInnerCondition3() { check("void f3(char c) { if(c=='x') if(c=='y') {}}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f4(char *p) { if(*p=='x') if(*p=='y') {}}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f5(const char * const p) { if(*p=='x') if(*p=='y') {}}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f5(const char * const p) { if('x'==*p) if('y'==*p) {}}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f6(char * const p) { if(*p=='x') if(*p=='y') {}}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f7(const char * p) { if(*p=='x') if(*p=='y') {}}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f8(int i) { if(i==4) if(i==2) {}}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f9(int *p) { if (*p==4) if(*p==2) {}}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f10(int * const p) { if (*p==4) if(*p==2) {}}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f11(const int *p) { if (*p==4) if(*p==2) {}}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f12(const int * const p) { if (*p==4) if(*p==2) {}}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("struct foo {\n" " int a;\n" " int b;\n" "};\n" "void f(foo x) { if(x.a==4) if(x.b==2) {}}"); ASSERT_EQUALS("", errout.str()); check("struct foo {\n" " int a;\n" " int b;\n" "};\n" "void f(foo x) { if(x.a==4) if(x.b==4) {}}"); ASSERT_EQUALS("", errout.str()); check("void f3(char a, char b) { if(a==b) if(a==0) {}}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) { if (x == 1) if (x != 1) {} }"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); } void oppositeInnerConditionAnd() { check("void f(int x) {\n" " if (a>3 && x > 100) {\n" " if (x < 10) {}\n" " }" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f(bool x, const int a, const int b) {\n" " if(x && a < b)\n" " if( x && a > b){}\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); } void oppositeInnerConditionEmpty() { check("void f1(const std::string &s) { if(s.size() > 42) if(s.empty()) {}}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f1(const std::string &s) { if(s.size() > 0) if(s.empty()) {}}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f1(const std::string &s) { if(s.size() < 0) if(s.empty()) {}} "); // <- CheckOther reports: checking if unsigned expression is less than zero ASSERT_EQUALS("", errout.str()); check("void f1(const std::string &s) { if(s.empty()) if(s.size() > 42) {}}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("template void f1(const T &s) { if(s.size() > 42) if(s.empty()) {}}"); ASSERT_EQUALS("", errout.str()); //We don't know the type of T so we don't know the relationship between size() and empty(). e.g. s might be a 50 tonne truck with nothing in it. check("void f2(const std::wstring &s) { if(s.empty()) if(s.size() > 42) {}}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f1(QString s) { if(s.isEmpty()) if(s.length() > 42) {}}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f1(const std::string &s, bool b) { if(s.empty() || ((s.size() == 1) && b)) {}}"); ASSERT_EQUALS("", errout.str()); check("void f1(const std::string &x, const std::string &y) { if(x.size() > 42) if(y.empty()) {}}"); ASSERT_EQUALS("", errout.str()); check("void f1(const std::string &x, const std::string &y) { if(y.empty()) if(x.size() > 42) {}}"); ASSERT_EQUALS("", errout.str()); check("void f1(const std::string v[10]) { if(v[0].size() > 42) if(v[1].empty()) {}}"); ASSERT_EQUALS("", errout.str()); check("void f1(const std::string &s) { if(s.size() <= 1) if(s.empty()) {}}"); ASSERT_EQUALS("", errout.str()); check("void f1(const std::string &s) { if(s.size() <= 2) if(s.empty()) {}}"); ASSERT_EQUALS("", errout.str()); check("void f1(const std::string &s) { if(s.size() < 2) if(s.empty()) {}}"); ASSERT_EQUALS("", errout.str()); check("void f1(const std::string &s) { if(s.size() >= 0) if(s.empty()) {}} "); // CheckOther says: Unsigned expression 's.size()' can't be negative so it is unnecessary to test it. [unsignedPositive] ASSERT_EQUALS("", errout.str()); // TODO: These are identical condition since size cannot be negative check("void f1(const std::string &s) { if(s.size() <= 0) if(s.empty()) {}}"); ASSERT_EQUALS("", errout.str()); // TODO: These are identical condition since size cannot be negative check("void f1(const std::string &s) { if(s.size() < 1) if(s.empty()) {}}"); ASSERT_EQUALS("", errout.str()); } void oppositeInnerConditionFollowVar() { check("struct X {\n" " void f() {\n" " const int flag = get();\n" " if (flag) {\n" " bar();\n" " if (!get()) {}\n" " }\n" " }\n" " void bar();\n" " int get() const;\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct CD {\n" " bool state;\n" " void foo() {\n" " const bool flag = this->get();\n" " if (flag) {\n" " this->bar();\n" " if (!this->get()) return;\n" " }\n" " }\n" " bool get() const;\n" " void bar();\n" "};\n"); ASSERT_EQUALS("", errout.str()); check("class C {\n" "public:\n" " bool f() const { return x > 0; }\n" " void g();\n" " int x = 0;\n" "};\n" "\n" "void C::g() {\n" " bool b = f();\n" " x += 1;\n" " if (!b && f()) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(double d) {\n" " if (d != 0) {\n" " int i = d;\n" " if (i == 0) {}\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void identicalInnerCondition() { check("void f1(int a, int b) { if(a==b) if(a==b) {}}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Identical inner 'if' condition is always true.\n", errout.str()); check("void f2(int a, int b) { if(a!=b) if(a!=b) {}}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Identical inner 'if' condition is always true.\n", errout.str()); // #6645 false negative: condition is always false check("void f(bool a, bool b) {\n" " if(a && b) {\n" " if(a) {}\n" " else {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Identical inner 'if' condition is always true.\n", errout.str()); check("bool f(int a, int b) {\n" " if(a == b) { return a == b; }\n" " return false;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (warning) Identical inner 'return' condition is always true.\n", errout.str()); check("bool f(bool a) {\n" " if(a) { return a; }\n" " return false;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int* f(int* a, int * b) {\n" " if(a) { return a; }\n" " return b;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int* f(std::shared_ptr a, std::shared_ptr b) {\n" " if(a.get()) { return a.get(); }\n" " return b.get();\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct A { int * x; };\n" "int* f(A a, int * b) {\n" " if(a.x) { return a.x; }\n" " return b;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " uint32_t value;\n" " get_value(&value);\n" " int opt_function_capable = (value >> 28) & 1;\n" " if (opt_function_capable) {\n" " value = 0;\n" " get_value (&value);\n" " if ((value >> 28) & 1) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void identicalConditionAfterEarlyExit() { check("void f(int x) {\n" // #8137 " if (x > 100) { return; }\n" " if (x > 100) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Identical condition 'x>100', second condition is always false\n", errout.str()); check("bool f(int x) {\n" " if (x > 100) { return false; }\n" " return x > 100;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Identical condition and return expression 'x>100', return value is always false\n", errout.str()); check("void f(int x) {\n" " if (x > 100) { return; }\n" " if (x > 100 || y > 100) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Identical condition 'x>100', second condition is always false\n", errout.str()); check("void f(int x) {\n" " if (x > 100) { return; }\n" " if (x > 100 && y > 100) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Identical condition 'x>100', second condition is always false\n", errout.str()); check("void f(int x) {\n" " if (x > 100) { return; }\n" " if (abc) {}\n" " if (x > 100) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (warning) Identical condition 'x>100', second condition is always false\n", errout.str()); check("void f(int x) {\n" " if (x > 100) { return; }\n" " while (abc) { y = x; }\n" " if (x > 100) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (warning) Identical condition 'x>100', second condition is always false\n", errout.str()); check("void f(int x) {\n" // #8217 - crash for incomplete code " if (x > 100) { return; }\n" " X(do);\n" " if (x > 100) {}\n" "}"); // TODO: we should probably throw unknownMacro InternalError. Complain that the macro X must be defined. We can't check the code well without the definition. ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (style) Condition 'x>100' is always false\n", errout.str()); check("void f(const int *i) {\n" " if (!i) return;\n" " if (!num1tok) { *num1 = *num2; }\n" " if (!i) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (warning) Identical condition '!i', second condition is always false\n", errout.str()); check("void C::f(Tree &coreTree) {\n" // daca " if(!coreTree.build())\n" " return;\n" " coreTree.dostuff();\n" " if(!coreTree.build()) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct C { void f(const Tree &coreTree); };\n" "void C::f(const Tree &coreTree) {\n" " if(!coreTree.build())\n" " return;\n" " coreTree.dostuff();\n" " if(!coreTree.build()) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (warning) Identical condition '!coreTree.build()', second condition is always false\n", errout.str()); check("void f(int x) {\n" // daca: labplot " switch(type) {\n" " case 1:\n" " if (x == 0) return 1;\n" " else return 2;\n" " case 2:\n" " if (x == 0) return 3;\n" " else return 4;\n" " }\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("static int failed = 0;\n" "void f() {\n" " if (failed) return;\n" " checkBuffer();\n" " if (failed) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // daca icu check("void f(const uint32_t *section, int32_t start) {\n" " if(10<=section[start]) { return; }\n" " if(++start<100 && 10<=section[start]) { }\n" "}"); ASSERT_EQUALS("", errout.str()); // daca iqtree check("void readNCBITree(std::istream &in) {\n" " char ch;\n" " in >> ch;\n" " if (ch != '|') return;\n" " in >> ch;\n" " if (ch != '|') {}\n" "}"); ASSERT_EQUALS("", errout.str()); // #8924 check("struct A {\n" " void f() {\n" " if (this->FileIndex >= 0) return;\n" " this->FileIndex = 1 ;\n" " if (this->FileIndex < 0) return;\n" " }\n" " int FileIndex;\n" "};"); ASSERT_EQUALS("[test.cpp:5]: (style) Condition 'this->FileIndex<0' is always false\n", errout.str()); // #8858 - #if check("short Do() {\n" " short ret = bar1();\n" " if ( ret )\n" " return ret;\n" "#ifdef FEATURE\n" " ret = bar2();\n" "#endif\n" " return ret;\n" "}"); ASSERT_EQUALS("", errout.str()); // #10456 check("int f() {\n" " int i = 0;\n" " auto f = [&](bool b) { if (b) ++i; };\n" " if (i) return i;\n" " f(true);\n" " if (i) return i;\n" " return 0;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void innerConditionModified() { check("void f(int x, int y) {\n" " if (x == 0) {\n" " x += y;\n" " if (x == 0) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if (x == 0) {\n" " x += y;\n" " if (x == 1) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int * x, int * y) {\n" " if (x[*y] == 0) {\n" " (*y)++;\n" " if (x[*y] == 0) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } // clarify conditions with = and comparison void clarifyCondition1() { check("void f() {\n" " if (x = b() < 0) {}\n" // don't simplify and verify this code "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Suspicious condition (assignment + comparison); Clarify expression with parentheses.\n", errout.str()); check("void f(int i) {\n" " for (i = 0; i < 10; i++) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " x = a(); if (x) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " if (x = b < 0 ? 1 : 2) {}\n" // don't simplify and verify this code "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int y = rand(), z = rand();\n" " if (y || (!y && z));\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Redundant condition: !y. 'y || (!y && z)' is equivalent to 'y || z'\n", errout.str()); check("void f() {\n" " int y = rand(), z = rand();\n" " if (y || !y && z);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Redundant condition: !y. 'y || (!y && z)' is equivalent to 'y || z'\n", errout.str()); check("void f() {\n" " if (!a || a && b) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: a. '!a || (a && b)' is equivalent to '!a || b'\n", errout.str()); check("void f(const Token *tok) {\n" " if (!tok->next()->function() ||\n" " (tok->next()->function() && tok->next()->function()->isConstructor()));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: tok->next()->function(). '!A || (A && B)' is equivalent to '!A || B'\n", errout.str()); check("void f() {\n" " if (!tok->next()->function() ||\n" " (!tok->next()->function() && tok->next()->function()->isConstructor()));\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " if (!tok->next()->function() ||\n" " (!tok2->next()->function() && tok->next()->function()->isConstructor()));\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(const Token *tok) {\n" " if (!tok->next(1)->function(1) ||\n" " (tok->next(1)->function(1) && tok->next(1)->function(1)->isConstructor()));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: tok->next(1)->function(1). '!A || (A && B)' is equivalent to '!A || B'\n", errout.str()); check("void f() {\n" " if (!tok->next()->function(1) ||\n" " (tok->next()->function(2) && tok->next()->function()->isConstructor()));\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int y = rand(), z = rand();\n" " if (y==0 || y!=0 && z);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Redundant condition: y!=0. 'y==0 || (y!=0 && z)' is equivalent to 'y==0 || z'\n", errout.str()); check("void f() {\n" " if (x>0 || (x<0 && y)) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // Test Token::expressionString, TODO move this test check("void f() {\n" " if (!dead || (dead && (*it).ticks > 0)) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: dead. '!dead || (dead && (*it).ticks>0)' is equivalent to '!dead || (*it).ticks>0'\n", errout.str()); check("void f() {\n" " if (!x || (x && (2>(y-1)))) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: x. '!x || (x && 2>(y-1))' is equivalent to '!x || 2>(y-1)'\n", errout.str()); check("void f(bool a, bool b) {\n" " if (a || (a && b)) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: a. 'a || (a && b)' is equivalent to 'a'\n", errout.str()); check("void f(bool a, bool b) {\n" " if (a && (a || b)) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: a. 'a && (a || b)' is equivalent to 'a'\n", errout.str()); } // clarify conditions with bitwise operator and comparison void clarifyCondition2() { check("void f() {\n" " if (x & 3 == 2) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Suspicious condition (bitwise operator + comparison); Clarify expression with parentheses.\n" "[test.cpp:2]: (style) Boolean result is used in bitwise operation. Clarify expression with parentheses.\n" "[test.cpp:2]: (style) Condition 'x&3==2' is always false\n", errout.str()); check("void f() {\n" " if (a & fred1.x == fred2.y) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Suspicious condition (bitwise operator + comparison); Clarify expression with parentheses.\n" "[test.cpp:2]: (style) Boolean result is used in bitwise operation. Clarify expression with parentheses.\n" , errout.str()); } // clarify condition that uses ! operator and then bitwise operator void clarifyCondition3() { check("void f(int w) {\n" " if(!w & 0x8000) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Boolean result is used in bitwise operation. Clarify expression with parentheses.\n", errout.str()); check("void f(int w) {\n" " if((!w) & 0x8000) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " if (x == foo() & 2) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Boolean result is used in bitwise operation. Clarify expression with parentheses.\n", errout.str()); check("void f() {\n" " if (2 & x == foo()) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Boolean result is used in bitwise operation. Clarify expression with parentheses.\n", errout.str()); check("void f() {\n" " if (2 & (x == foo())) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(std::list &ints) { }"); ASSERT_EQUALS("", errout.str()); check("void f() { A a; }"); ASSERT_EQUALS("", errout.str()); check("void f() { a(x there are never templates ASSERT_EQUALS("[test.c:1]: (style) Boolean result is used in bitwise operation. Clarify expression with parentheses.\n", errout.str()); check("class A;", "test.cpp"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " if (result != (char *)&inline_result) { }\n" // don't simplify and verify cast "}"); ASSERT_EQUALS("", errout.str()); // #8495 check("void f(bool a, bool b) {\n" " C & a & b;\n" "}"); ASSERT_EQUALS("", errout.str()); } void clarifyCondition4() { // ticket #3110 check("typedef double SomeType;\n" "typedef std::pair PairType;\n" "struct S\n" "{\n" " bool operator()\n" " ( PairType const & left\n" " , PairType const & right) const\n" " {\n" " return left.first < right.first;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void clarifyCondition5() { // ticket #3609 (using | in template instantiation) check("template struct CWinTraits;\n" "CWinTraits::GetWndStyle(0);"); ASSERT_EQUALS("", errout.str()); } void clarifyCondition6() { check("template\n" "SharedPtr& operator=( SharedPtr const & r ) {\n" " px = r.px;\n" " return *this;\n" "}"); ASSERT_EQUALS("", errout.str()); } void clarifyCondition7() { // Ensure that binary and unary &, and & in declarations are distinguished properly check("void f(bool error) {\n" " bool & withoutSideEffects=found.first->second;\n" // Declaring a reference to a boolean; & is no operator at all " execute(secondExpression, &programMemory, &result, &error);\n" // Unary & "}"); ASSERT_EQUALS("", errout.str()); } void clarifyCondition8() { // don't warn when boolean result comes from function call, array index, etc // the operator precedence is not unknown then check("bool a();\n" "bool f(bool b) {\n" " return (a() & b);\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f(bool *a, bool b) {\n" " return (a[10] & b);\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct A { bool a; };\n" "bool f(struct A a, bool b) {\n" " return (a.a & b);\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct A { bool a; };\n" "bool f(struct A a, bool b) {\n" " return (A::a & b);\n" "}"); ASSERT_EQUALS("", errout.str()); } void testBug5895() { check("void png_parse(uint64_t init, int buf_size) {\n" " if (init == 0x89504e470d0a1a0a || init == 0x8a4d4e470d0a1a0a)\n" " ;\n" "}"); ASSERT_EQUALS("", errout.str()); } void testBug5309() { check("extern uint64_t value;\n" "void foo() {\n" " if( ( value >= 0x7ff0000000000001ULL )\n" " && ( value <= 0x7fffffffffffffffULL ) );\n" "}"); ASSERT_EQUALS("", errout.str()); } void alwaysTrue() { check("void f(const struct S *s) {\n" //#8196 " int x1 = s->x;\n" " int x2 = s->x;\n" " if (x1 == 10 && x2 == 10) {}\n" // << "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:4]: (style) Condition 'x2==10' is always true\n", errout.str()); check("void f ()\n"// #8220 "{\n" " int a;\n" " int b = 0;\n" " int ret;\n" " \n" " a = rand();\n" " while (((0 < a) && (a < 2)) && ((8 < a) && (a < 10))) \n" " {\n" " b += a;\n" " a ++;\n" " }\n" " ret = b;\n" "}"); ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:8]: (style) Condition '8 [test.cpp:2]: (style) Condition 'x==0' is always false\n", errout.str()); check("void f() {\n" // #6898 (Token::expressionString) " int x = 0;\n" " A(x++ == 1);\n" " A(x++ == 2);\n" "}"); TODO_ASSERT_EQUALS("function argument is always true? however is code really weird/suspicious?", "", errout.str()); check("bool foo(int bar) {\n" " bool ret = false;\n" " if (bar == 1)\n" " return ret;\n" // <- #9326 - FP condition is always false " if (bar == 2)\n" " ret = true;\n" " return ret;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f1(const std::string &s) { if(s.empty()) if(s.size() == 0) {}}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (style) Condition 's.size()==0' is always true\n", errout.str()); check("void f() {\n" " int buf[42];\n" " if( buf != 0) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'buf!=0' is always true\n", errout.str()); // #8924 check("void f() {\n" " int buf[42];\n" " if( !buf ) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Condition '!buf' is always false\n", errout.str()); check("void f() {\n" " int buf[42];\n" " bool b = buf;\n" " if( b ) {}\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Condition 'b' is always true\n", errout.str()); check("void f() {\n" " int buf[42];\n" " bool b = buf;\n" " if( !b ) {}\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Condition '!b' is always false\n", errout.str()); check("void f() {\n" " int buf[42];\n" " int * p = nullptr;\n" " if( buf == p ) {}\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Condition 'buf==p' is always false\n", errout.str()); check("void f(bool x) {\n" " int buf[42];\n" " if( buf || x ) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'buf' is always true\n", errout.str()); check("void f(int * p) {\n" " int buf[42];\n" " if( buf == p ) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int buf[42];\n" " int p[42];\n" " if( buf == p ) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int buf[42];\n" " if( buf == 1) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // Avoid FP when condition comes from macro check("#define NOT !\n" "void f() {\n" " int x = 0;\n" " if (a) { return; }\n" // <- this is just here to fool simplifyKnownVariabels " if (NOT x) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("#define M x != 0\n" "void f() {\n" " int x = 0;\n" " if (a) { return; }\n" // <- this is just here to fool simplifyKnownVariabels " if (M) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("#define IF(X) if (X && x())\n" "void f() {\n" " IF(1) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // Avoid FP for sizeof condition check("void f() {\n" " if (sizeof(char) != 123) {}\n" " if (123 != sizeof(char)) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int x = 123;\n" " if (sizeof(char) != x) {}\n" " if (x != sizeof(char)) {}\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'sizeof(char)!=x' is always true\n" "[test.cpp:4]: (style) Condition 'x!=sizeof(char)' is always true\n", "", errout.str()); // Don't warn in assertions. Condition is often 'always true' by intention. // If platform,defines,etc cause an 'always false' assertion then that is not very dangerous neither check("void f() {\n" " int x = 0;\n" " assert(x == 0);\n" "}"); ASSERT_EQUALS("", errout.str()); // #9363 - do not warn about value passed to function check("void f(bool b) {\n" " if (b) {\n" " if (bar(!b)) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #7783 FP knownConditionTrueFalse on assert(0 && "message") check("void foo(int x) {\n" " if (x<0)\n" " {\n" " assert(0 && \"bla\");\n" " ASSERT(0 && \"bla\");\n" " assert_foo(0 && \"bla\");\n" " ASSERT_FOO(0 && \"bla\");\n" " assert((int)(0==0));\n" " assert((int)(0==0) && \"bla\");\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #7750 char literals in boolean expressions check("void f() {\n" " if('a'){}\n" " if(L'b'){}\n" " if(1 && 'c'){}\n" " int x = 'd' ? 1 : 2;\n" "}"); ASSERT_EQUALS("", errout.str()); // #8206 - knownCondition always false check("void f(int i)\n" "{\n" " if(i > 4)\n" " for( int x = 0; i < 3; ++x){}\n" // << "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Condition 'i<3' is always false\n", errout.str()); // Skip literals check("void f() { if(true) {} }"); ASSERT_EQUALS("", errout.str()); check("void f() { if(false) {} }"); ASSERT_EQUALS("", errout.str()); check("void f() { if(!true) {} }"); ASSERT_EQUALS("", errout.str()); check("void f() { if(!false) {} }"); ASSERT_EQUALS("", errout.str()); check("void f() { if(0) {} }"); ASSERT_EQUALS("", errout.str()); check("void f() { if(1) {} }"); ASSERT_EQUALS("", errout.str()); check("void f(int i) {\n" " bool b = false;\n" " if (i == 0) b = true;\n" " else if (!b && i == 1) {}\n" " if (b)\n" " {}\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Condition '!b' is always true\n", errout.str()); check("bool f() { return nullptr; }"); ASSERT_EQUALS("", errout.str()); check("enum E { A };\n" "bool f() { return A; }"); ASSERT_EQUALS("", errout.str()); check("bool f() {\n" " const int x = 0;\n" " return x;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f(void){return 1/abs(10);}"); ASSERT_EQUALS("", errout.str()); check("bool f() {\n" " int x = 0;\n" " return x;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f() {\n" " const int a = 50;\n" " const int b = 52;\n" " return a+b;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f() {\n" " int a = 50;\n" " int b = 52;\n" " a++;\n" " b++;\n" " return a+b;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool& g();\n" "bool f() {\n" " bool & b = g();\n" " b = false;\n" " return b;\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " bool b;\n" " bool f() {\n" " b = false;\n" " return b;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("bool f(long maxtime) {\n" " if (std::time(0) > maxtime)\n" " return std::time(0) > maxtime;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(double param) {\n" " while(bar()) {\n" " if (param<0.)\n" " return;\n" " }\n" " if (param<0.)\n" " return;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int i) {\n" " if (i==42)\n" " {\n" " bar();\n" " }\n" " if (cond && (42==i))\n" " return;\n" "}"); ASSERT_EQUALS("", errout.str()); // 8842 crash check("class a {\n" " int b;\n" " c(b);\n" " void f() {\n" " if (b) return;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("void f(const char* x, const char* t) {\n" " if (!(strcmp(x, y) == 0)) { return; }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(const int a[]){ if (a == 0){} }"); ASSERT_EQUALS("", errout.str()); check("struct S {\n" " bool operator<(const S&);\n" "};\n" "int main() {\n" " S s;\n" " bool c = s [test.cpp:3]: (style) Condition 'handle' is always true\n", errout.str()); check("int f(void *handle) {\n" " if (handle == 0) return 0;\n" " if (handle) return 1;\n" " else return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'handle' is always true\n", errout.str()); check("int f(void *handle) {\n" " if (handle != 0) return 0;\n" " if (handle) return 1;\n" " else return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Identical condition 'handle!=0', second condition is always false\n", errout.str()); check("void f(void* x, void* y) {\n" " if (x == nullptr && y == nullptr)\n" " return;\n" " if (x == nullptr || y == nullptr)\n" " return;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void* g();\n" "void f(void* a, void* b) {\n" " while (a) {\n" " a = g();\n" " if (a == b)\n" " break;\n" " }\n" " if (a) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void* g();\n" "void f(void* a, void* b) {\n" " while (a) {\n" " a = g();\n" " }\n" " if (a) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (style) Condition 'a' is always false\n", errout.str()); check("void f(int * x, bool b) {\n" " if (!x && b) {}\n" " else if (x) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " const std::string x=\"xyz\";\n" " if(!x.empty()){}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Condition '!x.empty()' is always true\n", errout.str()); check("std::string g();\n" "void f() {\n" " const std::string msg = g();\n" " if(!msg.empty()){}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int *array, int size ) {\n" " for(int i = 0; i < size; ++i) {\n" " if(array == 0)\n" " continue;\n" " if(array){}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Condition 'array' is always true\n", errout.str()); check("void f(int *array, int size ) {\n" " for(int i = 0; i < size; ++i) {\n" " if(array == 0)\n" " continue;\n" " else if(array){}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Condition 'array' is always true\n", errout.str()); // #9277 check("int f() {\n" " constexpr bool x = true;\n" " if constexpr (x)\n" " return 0;\n" " else\n" " return 1;\n" "}"); ASSERT_EQUALS("", errout.str()); // #9954 check("void f() {\n" " const size_t a(8 * sizeof(short));\n" " const size_t b(8 * sizeof(int));\n" " if constexpr (a == 16 && b == 16) {}\n" " else if constexpr (a == 16 && b == 32) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #9319 check("struct S {\n" " int a;\n" " int b;\n" "};\n" "void g(S s, bool& x);\n" "void f() {\n" " bool x = false;\n" " g({0, 1}, x);\n" " if (x) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // #9318 check("class A {};\n" "class B : public A {};\n" "void f(A* x) {\n" " if (!x)\n" " return;\n" " auto b = dynamic_cast(x);\n" " if (b) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("int foo() {\n" " auto x = getX();\n" " if (x == nullptr)\n" " return 1;\n" " auto y = dynamic_cast(x)\n" " if (y == nullptr)\n" " return 2;\n" " return 3;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // handleKnownValuesInLoop check("bool g();\n" "void f(bool x) {\n" " if (x) while(x) x = g();\n" "}"); ASSERT_EQUALS("", errout.str()); // isLikelyStream check("void f(std::istringstream& iss) {\n" " std::string x;\n" " while (iss) {\n" " iss >> x;\n" " if (!iss) break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #9332 check("struct A { void* g(); };\n" "void f() {\n" " A a;\n" " void* b = a.g();\n" " if (!b) return;\n" " void* c = a.g();\n" " if (!c) return;\n" " bool compare = c == b;\n" "}"); ASSERT_EQUALS("", errout.str()); // #9361 check("void f(char c) {\n" " if (c == '.') {}\n" " else if (isdigit(c) != 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // #9351 check("int f(int x) {\n" " const bool b = x < 42;\n" " if(b) return b?0:-1;\n" " return 42;\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3]: (style) Condition 'b' is always true\n", errout.str()); // #9362 check("uint8_t g();\n" "void f() {\n" " const uint8_t v = g();\n" " if((v != 0x00)) {\n" " if( (v & 0x01) == 0x00) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #9367 check("void f(long x) {\n" " if (x <= 0L)\n" " return;\n" " if (x % 360L == 0)\n" " return;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f(int a, int b) {\n" " static const int x = 10;\n" " return x == 1 ? a : b;\n" "}"); ASSERT_EQUALS("", errout.str()); check("const bool x = false;\n" "void f() {\n" " if (x) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("const bool x = false;\n" "void f() {\n" " if (!x) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // #9709 check("void f(int a) {\n" " bool ok = false;\n" " const char * r = nullptr;\n" " do_something(&r);\n" " if (r != nullptr)\n" " ok = a != 0;\n" " if (ok) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // #9816 check("bool g();\n" "void f() {\n" " bool b = false;\n" " do {\n" " do {\n" " if (g())\n" " break;\n" " b = true;\n" " } while(false);\n" " } while(!b);\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #9865 check("void f(const std::string &s) {\n" " for (std::string::const_iterator it = s.begin(); it != s.end(); ++it) {\n" " const unsigned char c = static_cast(*it);\n" " if (c == '0') {}\n" " else if ((c == 'a' || c == 'A')\n" " || (c == 'b' || c == 'B')) {}\n" " else {}\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #9711 check("int main(int argc, char* argv[]) {\n" " int foo = 0;\n" " struct option options[] = {\n" " {\"foo\", no_argument, &foo, \'f\'},\n" " {NULL, 0, NULL, 0},\n" " };\n" " getopt_long(argc, argv, \"f\", options, NULL);\n" " if (foo) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct a {\n" " int *b();\n" "};\n" "bool g(a c, a* d) {\n" " a *v, *e = v = &c;\n" " if (!v)\n" " return true;\n" " int *f = v->b();\n" " if (f)\n" " v = nullptr;\n" " if (v == nullptr && e) {}\n" " return d;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #10037 check("struct a {\n" " int* p;\n" "};\n" "void g(a*);\n" "void f() {\n" " struct a b;\n" " uint32_t p = (uint32_t) -1;\n" " b.p = (void *) &p;\n" " int r = g(&b);\n" " if (r == 0)\n" " if (p != (uint32_t) -1) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #9890 check("int g(int);\n" "bool h(int*);\n" "int f(int *x) {\n" " int y = g(0);\n" " if (!y) {\n" " if (h(x)) {\n" " y = g(1);\n" " if (y) {}\n" " return 0;\n" " }\n" " if (!y) {}\n" " }\n" " return 0;\n" "}\n"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:11]: (style) Condition '!y' is always true\n", errout.str()); // #10134 check("bool foo(bool b);\n" "bool thud(const std::vector& Arr, const std::wstring& Str) {\n" " if (Arr.empty() && Str.empty())\n" " return false;\n" " bool OldFormat = Arr.empty() && !Str.empty();\n" " if (OldFormat)\n" " return foo(OldFormat);\n" " return false;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #10208 check("bool GetFirst(std::string &first);\n" "bool GetNext(std::string &next);\n" "void g(const std::string& name);\n" "void f() {\n" " for (std::string name; name.empty() ? GetFirst(name) : GetNext(name);)\n" " g(name);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("bool GetFirst(std::string &first);\n" "bool GetNext(std::string &next);\n" "void g(const std::string& name);\n" "void f() {\n" " for (std::string name{}; name.empty() ? GetFirst(name) : GetNext(name);)\n" " g(name);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("bool GetFirst(std::string &first);\n" "bool GetNext(std::string &next);\n" "void g(const std::string& name);\n" "void f() {\n" " for (std::string name{'a', 'b'}; name.empty() ? GetFirst(name) : GetNext(name);)\n" " g(name);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("bool GetFirst(const std::string &first);\n" "bool GetNext(const std::string &next);\n" "void g(const std::string& name);\n" "void f() {\n" " for (std::string name; name.empty() ? GetFirst(name) : GetNext(name);)\n" " g(name);\n" "}\n"); ASSERT_EQUALS("[test.cpp:5]: (style) Condition 'name.empty()' is always true\n", errout.str()); // #10278 check("void foo(unsigned int x) {\n" " if ((100 - x) > 0) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #10298 check("void foo(unsigned int x) {\n" " if (x == -1) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #10121 check("struct AB {\n" " int a;\n" "};\n" "struct ABC {\n" " AB* ab;\n" "};\n" "void g(ABC*);\n" "int f(struct ABC *abc) {\n" " int err = 0;\n" " AB *ab = abc->ab;\n" " if (ab->a == 123){\n" " g(abc);\n" " if (ab->a != 123) {\n" " err = 1;\n" " }\n" " }\n" " return err;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #10323 check("void foo(int x) {\n" " if(x)\n" " if(x == 1) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void foo(int x) {\n" " if(x) {}\n" " else\n" " if(x == 1) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (style) Condition 'x==1' is always false\n", errout.str()); // do not report both unsignedLessThanZero and knownConditionTrueFalse check("void foo(unsigned int max) {\n" " unsigned int num = max - 1;\n" " if (num < 0) {}\n" // <- do not report knownConditionTrueFalse "}"); ASSERT_EQUALS("", errout.str()); // #10297 check("void foo(size_t len, int start) {\n" " if (start < 0) {\n" " start = len+start;\n" " if (start < 0) {}\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #10362 check("int tok;\n" "void next();\n" "void parse_attribute() {\n" " if (tok == '(') {\n" " int parenthesis = 0;\n" " do {\n" " if (tok == '(')\n" " parenthesis++;\n" " else if (tok == ')')\n" " parenthesis--;\n" " next();\n" " } while (parenthesis && tok != -1);\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #7843 check("void f(int i) {\n" " if(abs(i) == -1) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (style) Condition 'abs(i)==-1' is always false\n", errout.str()); // #7844 check("void f(int i) {\n" " if(i > 0 && abs(i) == i) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (style) Condition 'abs(i)==i' is always true\n", errout.str()); check("void f(int i) {\n" " if(i < 0 && abs(i) == i) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (style) Condition 'abs(i)==i' is always false\n", errout.str()); check("void f(int i) {\n" " if(i > -3 && abs(i) == i) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #9948 check("bool f(bool a, bool b) {\n" " return a || ! b || ! a;\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (style) Condition '!a' is always true\n", errout.str()); // #10148 check("void f(int i) {\n" " if (i >= 64) {}\n" " else if (i >= 32) {\n" " i &= 31;\n" " if (i == 0) {}\n" " else {}\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #10548 check("void f() {\n" " int i = 0;\n" " do {} while (i++ == 0);\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #10582 check("static void fun(message_t *message) {\n" " if (message->length >= 1) {\n" " switch (data[0]) {}\n" " }\n" " uint8_t d0 = message->length > 0 ? data[0] : 0xff;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #8266 check("void f(bool b) {\n" " if (b)\n" " return;\n" " if (g(&b) || b)\n" " return;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #9720 check("bool bar(int &);\n" "void f(int a, int b) {\n" " if (a + b == 3)\n" " return;\n" " if (bar(a) && (a + b == 3)) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #10437 check("void f() {\n" " Obj* PObj = nullptr;\n" " bool b = false;\n" " if (GetObj(PObj) && PObj != nullptr)\n" " b = true;\n" " if (b) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #10223 check("static volatile sig_atomic_t is_running;\n" "static void handler(int signum) {\n" " is_running = 0;\n" "}\n" "void f() {\n" " signal(SIGINT, &handler);\n" " is_running = 1;\n" " while (is_running) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #10659 check("auto func(const std::tuple& t) {\n" " auto& [foo, bar] = t;\n" " std::cout << foo << bar << std::endl;\n" " return foo < bar;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #10484 check("void f() {\n" " static bool init = true;\n" " if (init)\n" " init = false;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " static bool init(true);\n" " if (init)\n" " init = false;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " static bool init{ true };\n" " if (init)\n" " init = false;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #10248 check("void f() {\n" " static int var(1);\n" " if (var == 1) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " static int var{ 1 };\n" " if (var == 1) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void Fun();\n" "using Fn = void (*)();\n" "void f() {\n" " static Fn logger = nullptr;\n" " if (logger == nullptr)\n" " logger = Fun;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void Fun();\n" "using Fn = void (*)();\n" "void f() {\n" " static Fn logger(nullptr);\n" " if (logger == nullptr)\n" " logger = Fun;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void Fun();\n" "using Fn = void (*)();\n" "void f() {\n" " static Fn logger{ nullptr };\n" " if (logger == nullptr)\n" " logger = Fun;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void Fun();\n" "typedef void (*Fn)();\n" "void f() {\n" " static Fn logger = nullptr;\n" " if (logger == nullptr)\n" " logger = Fun;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void Fun();\n" "typedef void (*Fn)();\n" "void f() {\n" " static Fn logger(nullptr);\n" " if (logger == nullptr)\n" " logger = Fun;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void Fun();\n" "typedef void (*Fn)();\n" "void f() {\n" " static Fn logger{ nullptr };\n" " if (logger == nullptr)\n" " logger = Fun;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #9256 check("bool f() {\n" " bool b = false;\n" " b = true;\n" " return b;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #10702 check("struct Object {\n" " int _count=0;\n" " void increment() { ++_count;}\n" " auto get() const { return _count; }\n" "};\n" "struct Modifier {\n" "Object & _object;\n" " explicit Modifier(Object & object) : _object(object) {}\n" " void do_something() { _object.increment(); }\n" "};\n" "struct Foo {\n" " Object _object;\n" " void foo() {\n" " Modifier mod(_object);\n" " if (_object.get()>0)\n" " return;\n" " mod.do_something();\n" " if (_object.get()>0)\n" " return;\n" " }\n" "};\n"); ASSERT_EQUALS("", errout.str()); check("struct Object {\n" " int _count=0;\n" " auto get() const;\n" "};\n" "struct Modifier {\n" "Object & _object;\n" " explicit Modifier(Object & object);\n" " void do_something();\n" "};\n" "struct Foo {\n" " Object _object;\n" " void foo() {\n" " Modifier mod(_object);\n" " if (_object.get()>0)\n" " return;\n" " mod.do_something();\n" " if (_object.get()>0)\n" " return;\n" " }\n" "};\n"); ASSERT_EQUALS("", errout.str()); check("void f(const uint32_t u) {\n" " const uint32_t v = u < 4;\n" " if (v) {\n" " const uint32_t w = v < 2;\n" " if (w) {}\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:5]: (style) Condition 'w' is always true\n", errout.str()); check("void f(double d) {\n" // #10792 " if (d != 0) {\n" " int i = (int)d;\n" " if (i == 0) {}\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f(double d) {\n" " if (0 != d) {\n" " int i = (int)d;\n" " if (i == 0) {}\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct A { double d; }\n" "void f(A a) {\n" " if (a.d != 0) {\n" " int i = a.d;\n" " if (i == 0) {}\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " if(strlen(\"abc\") == 3) {;}\n" " if(strlen(\"abc\") == 1) {;}\n" " if(wcslen(L\"abc\") == 3) {;}\n" " if(wcslen(L\"abc\") == 1) {;}\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (style) Condition 'strlen(\"abc\")==3' is always true\n" "[test.cpp:3]: (style) Condition 'strlen(\"abc\")==1' is always false\n" "[test.cpp:4]: (style) Condition 'wcslen(L\"abc\")==3' is always true\n" "[test.cpp:5]: (style) Condition 'wcslen(L\"abc\")==1' is always false\n", errout.str()); check("int foo(bool a, bool b) {\n" " if(!a && b && (!a == !b))\n" " return 1;\n" " return 0;\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (style) Condition '!a==!b' is always false\n", errout.str()); // #10454 check("struct S {\n" " int f() const { return g() ? 0 : 1; }\n" " bool g() const { return u == 18446744073709551615ULL; }\n" " unsigned long long u{};\n" "};\n"); ASSERT_EQUALS("", errout.str()); // #8358 check("void f(double d) { if ((d * 0) != 0) {} }"); ASSERT_EQUALS("", errout.str()); // #6870 check("struct S {\n" " int* p;\n" " void f() const;\n" " int g();\n" "};\n" "void S::f() {\n" " if ((p == NULL) || ((p) && (g() >= *p))) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:7]: (style) Condition 'p' is always true\n", errout.str()); // #10749 check("struct Interface {\n" " virtual int method() = 0;\n" "};\n" "struct Child : Interface {\n" " int method() override { return 0; }\n" " auto foo() {\n" " if (method() == 0)\n" " return true;\n" " else\n" " return false;\n" " }\n" "};\n" "struct GrandChild : Child {\n" " int method() override { return 1; }\n" "};\n"); ASSERT_EQUALS("", errout.str()); // #6855 check("struct S { int i; };\n" "void f(S& s) {\n" " if (!(s.i > 0) && (s.i != 0))\n" " s.i = 0;\n" " else if (s.i < 0)\n" " s.s = 0;\n" "}\n"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (style) Condition 's.i<0' is always false\n", errout.str()); // #6857 check("int bar(int i) { return i; }\n" "void foo() {\n" " if (bar(1) == 0 && bar(1) > 0) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'bar(1)==0' is always false\n" "[test.cpp:3]: (style) Condition 'bar(1)>0' is always true\n", errout.str()); check("struct S { int bar(int i) const; };\n" "void foo(const S& s) {\n" " if (s.bar(1) == 0 && s.bar(1) > 0) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (warning) Logical conjunction always evaluates to false: s.bar(1) == 0 && s.bar(1) > 0.\n", errout.str()); check("struct B {\n" // #10618 " void Modify();\n" " static void Static();\n" " virtual void CalledByModify();\n" "};\n" "struct D : B {\n" " int i{};\n" " void testV();\n" " void testS();\n" " void CalledByModify() override { i = 0; }\n" "};\n" "void D::testV() {\n" " i = 1;\n" " B::Modify();\n" " if (i == 1) {}\n" "}\n" "void D::testS() {\n" " i = 1;\n" " B::Static();\n" " if (i == 1) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:20]: (style) Condition 'i==1' is always true\n", errout.str()); } void alwaysTrueSymbolic() { check("void f(const uint32_t x) {\n" " uint32_t y[1];\n" " y[0]=x;\n" " if(x > 0 || y[0] < 42){}\n" "}\n"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:4]: (style) Condition 'y[0]<42' is always true\n", errout.str()); check("void f(int x, int y) {\n" " if(x < y && x < 42) {\n" " --x;\n" " if(x == y) {}\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (style) Condition 'x==y' is always false\n", errout.str()); check("void f(bool a, bool b) { if (a == b && a && !b){} }"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (style) Condition '!b' is always false\n", errout.str()); check("bool f(bool a, bool b) { if(a && b && (!a)){} }"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (style) Condition '!a' is always false\n", errout.str()); check("void f(int x, int y) {\n" " if (x < y) {\n" " auto z = y - x;\n" " if (z < 1) {}\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (style) Condition 'z<1' is always false\n", errout.str()); check("bool f(int &index, const int s, const double * const array, double & x) {\n" " if (index >= s)\n" " return false;\n" " else {\n" " x = array[index];\n" " return (index++) >= s;\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:6]: (style) Condition '(index++)>=s' is always false\n", errout.str()); check("struct a {\n" " a *b() const;\n" "} c;\n" "void d() {\n" " a *e = nullptr;\n" " e = c.b();\n" " if (e) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("int g(int i) {\n" " if (i < 256)\n" " return 1;\n" " const int N = 2 * i;\n" " i -= 256;\n" " if (i == 0)\n" " return 0;\n" " return N;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f(int i, int j) {\n" " if (i < j) {\n" " i++;\n" " if (i >= j)\n" " return;\n" " i++;\n" " if (i >= j) {}\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("int get_delta() {\n" " clock_t now_ms = (clock() / (CLOCKS_PER_SEC / 1000));\n" " static clock_t last_clock_ms = now_ms;\n" " clock_t delta = now_ms - last_clock_ms;\n" " last_clock_ms = now_ms;\n" " if (delta > 50)\n" " delta = 50;\n" " return delta;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #10555 check("struct C {\n" " int GetI() const { return i; }\n" " int i{};\n" "};\n" "struct B {\n" " C *m_PC{};\n" " Modify();\n" "};\n" "struct D : B {\n" " void test(); \n" "};\n" "void D::test() {\n" " const int I = m_PC->GetI();\n" " Modify();\n" " if (m_PC->GetI() != I) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #10624 check("struct Data {\n" " Base* PBase{};\n" "};\n" "void f(Data* BaseData) {\n" " Base* PObj = BaseData->PBase;\n" " if (PObj == nullptr)\n" " return;\n" " Derived* pD = dynamic_cast(PObj);\n" " if (pD) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #9549 check("void f(const uint32_t v) {\n" " const uint32_t v16 = v >> 16;\n" " if (v16) {\n" " const uint32_t v8 = v16 >> 8;\n" " if (v8) {}\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #10649 check("void foo(struct diag_msg *msg) {\n" " msg = msg->next;\n" " if (msg == NULL)\n" " return CMD_OK;\n" " msg = msg->next;\n" " if (msg == NULL)\n" " return CMD_OK;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("int foo(bool a, bool b) {\n" " if((!a == !b) && !a && b)\n" " return 1;\n" " return 0;\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (style) Condition 'b' is always false\n", errout.str()); // #11124 check("struct Basket {\n" " std::vector getApples() const;\n" " std::vector getBananas() const; \n" "};\n" "int getFruit(const Basket & b, bool preferApples)\n" "{\n" " std::vector apples = b.getApples();\n" " int apple = apples.empty() ? -1 : apples.front();\n" " std::vector bananas = b.getBananas();\n" " int banana = bananas.empty() ? -1 : bananas.front();\n" " int fruit = std::max(apple, banana);\n" " if (fruit == -1)\n" " return fruit;\n" " if (std::min(apple, banana) != -1)\n" " fruit = preferApples ? apple : banana;\n" " return fruit;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f(const std::string & s, int i) {\n" " const char c = s[i];\n" " if (!std::isalnum(c)) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void alwaysTrueInfer() { check("void f(int x) {\n" " if (x > 5) {\n" " x++;\n" " if (x == 1) {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (style) Condition 'x==1' is always false\n", errout.str()); check("void f(int x) {\n" " if (x > 5) {\n" " x++;\n" " if (x != 1) {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (style) Condition 'x!=1' is always true\n", errout.str()); // #6890 check("void f(int i) {\n" " int x = i;\n" " if (x >= 1) {}\n" " else {\n" " x = 8 - x;\n" " if (x == -1) {}\n" " else {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (style) Condition 'x==-1' is always false\n", errout.str()); check("void f(int i) {\n" " int x = i;\n" " if (x >= 1) {}\n" " else {\n" " x = 8 - x;\n" " if (x != -1) {}\n" " else {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (style) Condition 'x!=-1' is always true\n", errout.str()); check("void f(int i) {\n" " int x = i;\n" " if (x >= 1) {}\n" " else {\n" " x = 8 - x;\n" " if (x >= -1) {}\n" " else {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (style) Condition 'x>=-1' is always true\n", errout.str()); check("void f(int i) {\n" " int x = i;\n" " if (x >= 1) {}\n" " else {\n" " x = 8 - x;\n" " if (x > -1) {}\n" " else {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (style) Condition 'x>-1' is always true\n", errout.str()); check("void f(int i) {\n" " int x = i;\n" " if (x >= 1) {}\n" " else {\n" " x = 8 - x;\n" " if (x < -1) {}\n" " else {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (style) Condition 'x<-1' is always false\n", errout.str()); check("void f(int i) {\n" " int x = i;\n" " if (x >= 1) {}\n" " else {\n" " x = 8 - x;\n" " if (x <= -1) {}\n" " else {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (style) Condition 'x<=-1' is always false\n", errout.str()); check("void f(int i) {\n" " int x = i;\n" " if (x >= 1) {}\n" " else {\n" " x = 8 - x;\n" " if (x > 7) {}\n" " else {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (style) Condition 'x>7' is always true\n", errout.str()); check("void f(int i) {\n" " int x = i;\n" " if (x >= 1) {}\n" " else {\n" " x = 8 - x;\n" " if (x > 9) {}\n" " else {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int i) {\n" " int x = i;\n" " if (x >= 1) {}\n" " else {\n" " x = 8 - x;\n" " if (x > 10) {}\n" " else {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #11100 check("struct T {\n" " bool m{};\n" " void f(bool b);\n" " bool get() const { return m; }\n" " void set(bool v) { m = v; }\n" "};\n" "void T::f(bool b) {\n" " bool tmp = get();\n" " set(b);\n" " if (tmp != get()) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #9541 check("int f(int pos, int a) {\n" " if (pos <= 0)\n" " pos = 0;\n" " else if (pos < a)\n" " if(pos > 0)\n" " --pos;\n" " return pos;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:5]: (style) Condition 'pos>0' is always true\n", errout.str()); // #9721 check("void f(int x) {\n" " if (x > 127) {\n" " if ( (x>255) || (-128>x) )\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Condition '-128>x' is always false\n", errout.str()); // #8778 check("void f() {\n" " for(int i = 0; i < 19; ++i)\n" " if(i<=18) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'i<=18' is always true\n", errout.str()); // #8209 check("void f() {\n" " for(int x = 0; x < 3; ++x)\n" " if(x == -5) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'x==-5' is always false\n", errout.str()); // #8407 check("int f(void) {\n" " for(int i = 0; i <1; ++i)\n" " if(i == 0) return 1; \n" // << " else return 0;\n" " return -1;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'i==0' is always true\n", errout.str()); check("void f(unsigned int u1, unsigned int u2) {\n" " if (u1 <= 10 && u2 >= 20) {\n" " if (u1 != u2) {}\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Condition 'u1!=u2' is always true\n", errout.str()); // #10544 check("void f(int N) {\n" " if (N > 0) {\n" " while (N)\n" " N = test();\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #11098 check("void f(unsigned int x) { if (x == -1u) {} }\n"); ASSERT_EQUALS("", errout.str()); } void alwaysTrueContainer() { // #9329 check("void c1(std::vector&);\n" "void c2(std::vector&);\n" "void foo(int flag) {\n" " std::vector g;\n" " if (flag)\n" " c1(g );\n" " else\n" " c2(g );\n" " if ( !g.empty() )\n" " return;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int flag) {\n" " std::vector g;\n" " if (flag)\n" " c1(g );\n" " else\n" " c2(g );\n" " if ( !g.empty() )\n" " return;\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " std::vector v;\n" " void g();\n" " void f(bool b) {\n" " v.clear();\n" " g();\n" " return !v.empty();\n" " }\n" "};\n"); ASSERT_EQUALS("", errout.str()); // #10409 check("void foo(const std::string& s) {\n" " if( s.size() < 2 ) return;\n" " if( s == \"ab\" ) return;\n" " if( s.size() < 3 ) return;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void foo(const std::string& s) {\n" " if( s.size() < 2 ) return;\n" " if( s != \"ab\" )\n" " if( s.size() < 3 ) return;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #10226 check("int f(std::vector::iterator it, const std::vector& vector) {\n" " if (!(it != vector.end() && it != vector.begin()))\n" " throw 0;\n" " if (it != vector.end() && *it == 0)\n" " return -1;\n" " return *it;\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (style) Condition 'it!=vector.end()' is always true\n", errout.str()); } void alwaysTrueLoop() { check("long foo() {\n" " bool bUpdated = false;\n" " long Ret{};\n" " do {\n" " Ret = bar();\n" " if (Ret == 0) {\n" " if (bUpdated)\n" " return 1;\n" " bUpdated = true;\n" " }\n" " else\n" " bUpdated = false;\n" " }\n" " while (bUpdated);\n" " return Ret;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("bool foo() {\n" " bool bFirst = true;\n" " do {\n" " if (bFirst)\n" " bar();\n" " if (baz())\n" " break; \n" " bFirst = false;\n" " } while (true);\n" " return bFirst;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " void * pool = NULL;\n" " do {\n" " pool = malloc(40);\n" " if (dostuff())\n" " break;\n" " pool = NULL;\n" " }\n" " while (0);\n" " if (pool) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #10863 check("void f(const int A[], int Len) {\n" " if (Len <= 0)\n" " return;\n" " int I = 0;\n" " while (I < Len) {\n" " int K = I + 1;\n" " for (; K < Len; K++) {\n" " if (A[I] != A[K])\n" " break;\n" " } \n" " I = K; \n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void alwaysTrueTryCatch() { check("void g();\n" "void f(int x)\n" "{\n" " if( x ) {\n" " try {\n" " g();\n" " }\n" " catch(...) {\n" " return;\n" " }\n" " }\n" " g();\n" " if( x ) {\n" " g();\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void g();\n" "void h();\n" "void f(int x) {\n" " if( x ) {\n" " try {\n" " g();\n" " return;\n" " }\n" " catch( ... ) {}\n" " }\n" " h();\n" " if( x ) {\n" " g();\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void multiConditionAlwaysTrue() { check("void f() {\n" " int val = 0;\n" " if (val < 0) continue;\n" " if (val > 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int val = 0;\n" " if (val < 0) {\n" " if (val > 0) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int val = 0;\n" " if (val < 0) {\n" " if (val < 0) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int activate = 0;\n" " int foo = 0;\n" " if (activate) {}\n" " else if (foo) {}\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Condition 'activate' is always false\n" "[test.cpp:5]: (style) Condition 'foo' is always false\n", errout.str()); // #6904 check("void f() {\n" " const int b[2] = { 1,0 };\n" " if(b[1] == 2) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'b[1]==2' is always false\n", errout.str()); // #9878 check("void f(bool a, bool b) {\n" " if (a && b){;}\n" " else if (!a && b){;}\n" " else if (!a && !b){;}\n" " else {;}\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void duplicateCondition() { check("void f(bool x) {\n" " if(x) {}\n" " if(x) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The if condition is the same as the previous if condition\n", errout.str()); check("void f(int x) {\n" " if(x == 1) {}\n" " if(x == 1) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The if condition is the same as the previous if condition\n", errout.str()); check("void f(int x) {\n" " if(x == 1) {}\n" " if(x == 2) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if(x == 1) {}\n" " if(x != 1) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(bool x) {\n" " if(x) {}\n" " g();\n" " if(x) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if(x == 1) { x++; }\n" " if(x == 1) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // #8996 check("void g(int** v);\n" "void f() {\n" " int a = 0;\n" " int b = 0;\n" " int* d[] = {&a, &b};\n" " g(d);\n" " if (a) {}\n" " if (b) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // #9311 check("struct c {\n" " int* p;\n" "};\n" "void g(struct c* v);\n" "void f() {\n" " int a = 0;\n" " int b = 0;\n" " struct c d[] = {{&a}, {&b}};\n" " g(d);\n" " if (a) {}\n" " if (b) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // #8993 check("void f(const std::string& x) {\n" " auto y = x;\n" " if (x.empty()) y = \"1\";\n" " if (y.empty()) return;\n" "}"); ASSERT_EQUALS("", errout.str()); // #9106 check("struct A {int b;};\n" "void f(A a, int c) {\n" " if (a.b) a.b = c;\n" " if (a.b) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " int a;\n" " void b() const {\n" " return a == 1;\n" " }\n" " void c();\n" " void d() {\n" " if(b()) {\n" " c();\n" " }\n" " if (b()) {\n" " a = 3;\n" " }\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " int a;\n" " void b() const {\n" " return a == 1;\n" " }\n" " void d() {\n" " if(b()) {\n" " a = 2;\n" " }\n" " if (b()) {\n" " a = 3;\n" " }\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " int a;\n" " void b() const {\n" " return a == 1;\n" " }\n" " void d() {\n" " if(b()) {\n" " }\n" " if (b()) {\n" " a = 3;\n" " }\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:9]: (style) The if condition is the same as the previous if condition\n", errout.str()); check("void f(bool a, bool b) {\n" " auto g = [&] { b = !a; };\n" " if (b)\n" " g();\n" " if (b) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void g(bool& a);\n" "void f(bool b) {\n" " auto h = std::bind(&g, std::ref(b));\n" " if (b)\n" " h();\n" " if (b) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f(int *i) {\n" " if (*i == 0) {\n" " *i = 1;\n" " }\n" " if (*i == 0) {\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void g(std::function);\n" "void f(std::vector v) {\n" " auto x = [&v] { v.push_back(1); };\n" " if(v.empty()) {\n" " g(x);\n" " }\n" " if(v.empty())\n" " return;\n" " return;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct S { int i; };\n" "int f(const S& s) {\n" " int a = 0, b = 0;\n" " if (s.i == 0)\n" " a = 1;\n" " if (s.i == 0)\n" " b = 1;\n" " return a + b;\n" "}\n"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:6]: (style) The if condition is the same as the previous if condition\n", errout.str()); // do not crash check("void assign(const MMA& other) {\n" " if (mPA.cols != other.mPA.cols || mPA.rows != other.mPA.rows)\n" " ;\n" " if (other.mPA.cols > 0 && other.mPA.rows > 0)\n" " ;\n" "}"); } void checkInvalidTestForOverflow() { check("void f(char *p, unsigned int x) {\n" " assert((p + x) < p);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Invalid test for overflow '(p+x)= p);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Invalid test for overflow '(p+x)>=p'; pointer overflow is undefined behavior. Some mainstream compilers remove such overflow tests when optimising the code and assume it's always true.\n", errout.str()); check("void f(char *p, unsigned int x) {\n" " assert(p > (p + x));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Invalid test for overflow 'p>(p+x)'; pointer overflow is undefined behavior. Some mainstream compilers remove such overflow tests when optimising the code and assume it's always false.\n", errout.str()); check("void f(char *p, unsigned int x) {\n" " assert(p <= (p + x));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Invalid test for overflow 'p<=(p+x)'; pointer overflow is undefined behavior. Some mainstream compilers remove such overflow tests when optimising the code and assume it's always true.\n", errout.str()); check("void f(signed int x) {\n" // unsigned overflow => don't warn " assert(x + 100U < x);\n" "}"); ASSERT_EQUALS("", errout.str()); // x + c < x #define MSG(EXPR, RESULT) "[test.cpp:1]: (warning) Invalid test for overflow '" EXPR "'; signed integer overflow is undefined behavior. Some mainstream compilers remove such overflow tests when optimising the code and assume it's always " RESULT ".\n" check("int f(int x) { return x + 10 > x; }"); ASSERT_EQUALS(MSG("x+10>x", "true"), errout.str()); check("int f(int x) { return x + 10 >= x; }"); ASSERT_EQUALS(MSG("x+10>=x", "true"), errout.str()); check("int f(int x) { return x + 10 < x; }"); ASSERT_EQUALS(MSG("x+10 x; }"); ASSERT_EQUALS(MSG("x-10>x", "false"), errout.str()); check("int f(int x) { return x - 10 >= x; }"); ASSERT_EQUALS(MSG("x-10>=x", "false"), errout.str()); check("int f(int x) { return x - 10 < x; }"); ASSERT_EQUALS(MSG("x-10 x; }"); ASSERT_EQUALS(MSG("x+y>x", "y>0"), errout.str()); check("int f(int x, int y) { return x + y >= x; }"); ASSERT_EQUALS(MSG("x+y>=x", "y>=0"), errout.str()); // x - y < x check("int f(int x, int y) { return x - y < x; }"); ASSERT_EQUALS(MSG("x-y0"), errout.str()); check("int f(int x, int y) { return x - y <= x; }"); ASSERT_EQUALS(MSG("x-y<=x", "y>=0"), errout.str()); check("int f(int x, int y) { return x - y > x; }"); ASSERT_EQUALS(MSG("x-y>x", "y<0"), errout.str()); check("int f(int x, int y) { return x - y >= x; }"); ASSERT_EQUALS(MSG("x-y>=x", "y<=0"), errout.str()); } void checkConditionIsAlwaysTrueOrFalseInsideIfWhile() { check("void f() {\n" " enum states {A,B,C};\n" " const unsigned g_flags = B|C;\n" " if(g_flags & A) {}\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Condition 'g_flags&A' is always false\n", errout.str()); check("void f() {\n" " int a = 5;" " if(a) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Condition 'a' is always true\n", errout.str()); check("void f() {\n" " int a = 5;" " while(a + 1) { a--; }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int a = 5;" " while(a + 1) { return; }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Condition 'a+1' is always true\n", errout.str()); } void alwaysTrueFalseInLogicalOperators() { check("bool f();\n" "void foo() { bool x = true; if(x||f()) {}}"); ASSERT_EQUALS("[test.cpp:2]: (style) Condition 'x' is always true\n", errout.str()); check("void foo(bool b) { bool x = true; if(x||b) {}}"); ASSERT_EQUALS("[test.cpp:1]: (style) Condition 'x' is always true\n", errout.str()); check("void foo(bool b) { if(true||b) {}}"); ASSERT_EQUALS("", errout.str()); check("bool f();\n" "void foo() { bool x = false; if(x||f()) {}}"); ASSERT_EQUALS("[test.cpp:2]: (style) Condition 'x' is always false\n", errout.str()); check("bool f();\n" "void foo() { bool x = false; if(x&&f()) {}}"); ASSERT_EQUALS("[test.cpp:2]: (style) Condition 'x' is always false\n", errout.str()); check("void foo(bool b) { bool x = false; if(x&&b) {}}"); ASSERT_EQUALS("[test.cpp:1]: (style) Condition 'x' is always false\n", errout.str()); check("void foo(bool b) { if(false&&b) {}}"); ASSERT_EQUALS("", errout.str()); check("bool f();\n" "void foo() { bool x = true; if(x&&f()) {}}"); ASSERT_EQUALS("[test.cpp:2]: (style) Condition 'x' is always true\n", errout.str()); // #9578 check("bool f(const std::string &s) {\n" " return s.size()>2U && s[0]=='4' && s[0]=='2';\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (style) Condition 's[0]=='2'' is always false\n", errout.str()); } void pointerAdditionResultNotNull() { check("void f(char *ptr) {\n" " if (ptr + 1 != 0);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison is wrong. Result of 'ptr+1' can't be 0 unless there is pointer overflow, and pointer overflow is undefined behaviour.\n", errout.str()); } void duplicateConditionalAssign() { setMultiline(); check("void f(int& x, int y) {\n" " if (x == y)\n" " x = y;\n" "}"); ASSERT_EQUALS("test.cpp:3:style:Assignment 'x=y' is redundant with condition 'x==y'.\n" "test.cpp:2:note:Condition 'x==y'\n" "test.cpp:3:note:Assignment 'x=y' is redundant\n", errout.str()); check("void f(int& x, int y) {\n" " if (x != y)\n" " x = y;\n" "}"); ASSERT_EQUALS("test.cpp:2:style:The statement 'if (x!=y) x=y' is logically equivalent to 'x=y'.\n" "test.cpp:3:note:Assignment 'x=y'\n" "test.cpp:2:note:Condition 'x!=y' is redundant\n", errout.str()); check("void f(int& x, int y) {\n" " if (x == y)\n" " x = y;\n" " else\n" " x = 1;\n" "}"); ASSERT_EQUALS("test.cpp:3:style:Assignment 'x=y' is redundant with condition 'x==y'.\n" "test.cpp:2:note:Condition 'x==y'\n" "test.cpp:3:note:Assignment 'x=y' is redundant\n", errout.str()); check("void f(int& x, int y) {\n" " if (x != y)\n" " x = y;\n" " else\n" " x = 1;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int& x, int y) {\n" " if (x == y)\n" " x = y + 1;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void g();\n" "void f(int& x, int y) {\n" " if (x == y) {\n" " x = y;\n" " g();\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void checkAssignmentInCondition() { check("void f(std::string s) {\n" " if (s=\"123\"){}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Suspicious assignment in condition. Condition 's=\"123\"' is always true.\n", errout.str()); check("void f(std::string *p) {\n" " if (p=foo()){}\n" "}"); ASSERT_EQUALS("", errout.str()); } void compareOutOfTypeRange() { Settings settingsUnix64; settingsUnix64.severity.enable(Severity::style); settingsUnix64.platform(cppcheck::Platform::PlatformType::Unix64); check("void f(unsigned char c) {\n" " if (c == 256) {}\n" "}", &settingsUnix64); ASSERT_EQUALS("[test.cpp:2]: (style) Comparing expression of type 'unsigned char' against value 256. Condition is always false.\n", errout.str()); check("void f(unsigned char* b, int i) {\n" // #6372 " if (b[i] == 256) {}\n" "}", &settingsUnix64); ASSERT_EQUALS("[test.cpp:2]: (style) Comparing expression of type 'unsigned char' against value 256. Condition is always false.\n", errout.str()); check("void f(unsigned char c) {\n" " if (c == 255) {}\n" "}", &settingsUnix64); ASSERT_EQUALS("", errout.str()); check("void f(bool b) {\n" " if (b == true) {}\n" "}", &settingsUnix64); ASSERT_EQUALS("", errout.str()); // #10372 check("void f(signed char x) {\n" " if (x == 0xff) {}\n" "}", &settingsUnix64); ASSERT_EQUALS("[test.cpp:2]: (style) Comparing expression of type 'signed char' against value 255. Condition is always false.\n", errout.str()); check("void f(short x) {\n" " if (x == 0xffff) {}\n" "}", &settingsUnix64); ASSERT_EQUALS("[test.cpp:2]: (style) Comparing expression of type 'signed short' against value 65535. Condition is always false.\n", errout.str()); check("void f(int x) {\n" " if (x == 0xffffffff) {}\n" "}", &settingsUnix64); ASSERT_EQUALS("", errout.str()); check("void f(long x) {\n" " if (x == ~0L) {}\n" "}", &settingsUnix64); ASSERT_EQUALS("", errout.str()); check("void f(long long x) {\n" " if (x == ~0LL) {}\n" "}", &settingsUnix64); ASSERT_EQUALS("", errout.str()); check("int f(int x) {\n" " const int i = 0xFFFFFFFF;\n" " if (x == i) {}\n" "}", &settingsUnix64); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " char c;\n" " if ((c = foo()) != -1) {}\n" "}", &settingsUnix64); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if (x < 3000000000) {}\n" "}", &settingsUnix64); ASSERT_EQUALS("[test.cpp:2]: (style) Comparing expression of type 'signed int' against value 3000000000. Condition is always true.\n", errout.str()); check("void f(const signed char i) {\n" // #8545 " if (i > -129) {}\n" // warn " if (i >= -128) {}\n" // warn " if (i >= -127) {}\n" " if (i < +128) {}\n" // warn " if (i <= +127) {}\n" // warn " if (i <= +126) {}\n" "}\n", &settingsUnix64); ASSERT_EQUALS("[test.cpp:2]: (style) Comparing expression of type 'const signed char' against value -129. Condition is always true.\n" "[test.cpp:3]: (style) Comparing expression of type 'const signed char' against value -128. Condition is always true.\n" "[test.cpp:5]: (style) Comparing expression of type 'const signed char' against value 128. Condition is always true.\n" "[test.cpp:6]: (style) Comparing expression of type 'const signed char' against value 127. Condition is always true.\n", errout.str()); check("void f(const unsigned char u) {\n" " if (u > 0) {}\n" " if (u < 0) {}\n" // warn " if (u >= 0) {}\n" // warn " if (u <= 0) {}\n" " if (u > 255) {}\n" // warn " if (u < 255) {}\n" " if (u >= 255) {}\n" " if (u <= 255) {}\n" // warn " if (0 < u) {}\n" " if (0 > u) {}\n" // warn " if (0 <= u) {}\n" // warn " if (0 >= u) {}\n" " if (255 < u) {}\n" // warn " if (255 > u) {}\n" " if (255 <= u) {}\n" " if (255 >= u) {}\n" // warn "}\n", &settingsUnix64); ASSERT_EQUALS("[test.cpp:3]: (style) Comparing expression of type 'const unsigned char' against value 0. Condition is always false.\n" "[test.cpp:4]: (style) Comparing expression of type 'const unsigned char' against value 0. Condition is always true.\n" "[test.cpp:6]: (style) Comparing expression of type 'const unsigned char' against value 255. Condition is always false.\n" "[test.cpp:9]: (style) Comparing expression of type 'const unsigned char' against value 255. Condition is always true.\n" "[test.cpp:11]: (style) Comparing expression of type 'const unsigned char' against value 0. Condition is always false.\n" "[test.cpp:12]: (style) Comparing expression of type 'const unsigned char' against value 0. Condition is always true.\n" "[test.cpp:14]: (style) Comparing expression of type 'const unsigned char' against value 255. Condition is always false.\n" "[test.cpp:17]: (style) Comparing expression of type 'const unsigned char' against value 255. Condition is always true.\n", errout.str()); } void knownConditionCast() { // #9976 check("void f(int i) {\n" " if (i < 0 || (unsigned)i > 5) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void knownConditionIncrementLoop() { // #9808 check("void f() {\n" " int a = 0;\n" " while (++a < 5) {}\n" " if (a == 1) {}\n" " std::cout << a;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestCondition)