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() {\n" // #4842
" int x = 0;\n"
" if (a) { return; }\n" // <- this is just here to fool simplifyKnownVariabels
" if (!x) {}\n"
"}");
ASSERT_EQUALS("[test.cpp:4]: (style) Condition '!x' is always true\n", errout.str());
check("bool f(int x) {\n"
" if(x == 0) { x++; return x == 0; } \n"
" return false;\n"
"}");
ASSERT_EQUALS("[test.cpp:2] -> [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"
"}");
ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'x++==1' is always false\n"
"[test.cpp:4]: (style) Condition 'x++==2' is always false\n",
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());
// 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"
"}");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" int x = 123;\n"
" if (sizeof(char) != x) {}\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'sizeof(char)!=x' 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());
// #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"
"}\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());
// 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"
"}\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; }\n");
ASSERT_EQUALS("", errout.str());
check("bool f() { \n"
" const int x = 0;\n"
" return x; \n"
"}\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"
"}\n");
ASSERT_EQUALS("", errout.str());
check("bool f() {\n"
" const int a = 50;\n"
" const int b = 52;\n"
" return a+b;\n"
"}\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"
"}\n");
ASSERT_EQUALS("", errout.str());
check("bool& g();\n"
"bool f() {\n"
" bool & b = g();\n"
" b = false;\n"
" return b;\n"
"}\n");
ASSERT_EQUALS("", errout.str());
check("struct A {\n"
" bool b;\n"
" bool f() {\n"
" b = false;\n"
" return b;\n"
" }\n"
"};\n");
ASSERT_EQUALS("", errout.str());
check("bool f(long maxtime) {\n"
" if (std::time(0) > maxtime)\n"
" return std::time(0) > maxtime;\n"
"}\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"
"}\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"
"}\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"
"};\n");
ASSERT_EQUALS("", errout.str());
check("void f(const char* x, const char* t) {\n"
" if (!(strcmp(x, y) == 0)) { return; }\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"
"}\n");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" int val = 0;\n"
" if (val < 0) {\n"
" if (val > 0) {}\n"
" }\n"
"}\n");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" int val = 0;\n"
" if (val < 0) {\n"
" if (val < 0) {}\n"
" }\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"
"}\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());
}
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'. Condition is always true unless there is overflow, and overflow is undefined behaviour.\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)'. Condition is always false unless there is overflow, and overflow is undefined behaviour.\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)'. Condition is always true unless there is overflow, and overflow is undefined behaviour.\n", errout.str());
check("void f(signed int x) {\n"
" assert(x + 100 < x);\n"
"}");
ASSERT_EQUALS("[test.cpp:2]: (warning) Invalid test for overflow 'x+100 don't warn
" assert(x + 100U < x);\n"
"}");
ASSERT_EQUALS("", 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()) {}}\n");
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) {}}\n");
ASSERT_EQUALS("[test.cpp:1]: (style) Condition 'x' is always true\n", errout.str());
check("void foo(bool b) { if(true||b) {}}\n");
ASSERT_EQUALS("", errout.str());
check("bool f();\n"
"void foo() { bool x = false; if(x||f()) {}}\n");
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()) {}}\n");
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) {}}\n");
ASSERT_EQUALS("[test.cpp:1]: (style) Condition 'x' is always false\n", errout.str());
check("void foo(bool b) { if(false&&b) {}}\n");
ASSERT_EQUALS("", errout.str());
check("bool f();\n"
"void foo() { bool x = true; if(x&&f()) {}}\n");
ASSERT_EQUALS("[test.cpp:2]: (style) Condition 'x' is always true\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());
}
};
REGISTER_TEST(TestCondition)