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());
}
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());
// #6904
check("void f() {\n"
" const int b[2] = { 1,0 };\n"
" if(b[1] == 2) {}\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'b[1]==2' is always false\n", errout.str());
}
void duplicateCondition() {
check("void f(bool x) {\n"
" if(x) {}\n"
" if(x) {}\n"
"}\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"
"}\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"
"}\n");
ASSERT_EQUALS("", errout.str());
check("void f(int x) {\n"
" if(x == 1) {}\n"
" if(x != 1) {}\n"
"}\n");
ASSERT_EQUALS("", errout.str());
check("void f(bool x) {\n"
" if(x) {}\n"
" g();\n"
" if(x) {}\n"
"}\n");
ASSERT_EQUALS("", errout.str());
check("void f(int x) {\n"
" if(x == 1) { x++; }\n"
" if(x == 1) {}\n"
"}\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"
"}\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"
"}\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"
"}\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"
"}\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());
}
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());
// #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"
"}\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"
"}\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"
"}\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"
"}\n");
ASSERT_EQUALS("", errout.str());
check("void f(int& x, int y) {\n"
" if (x == y)\n"
" x = y + 1;\n"
"}\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"
"}\n");
ASSERT_EQUALS("", errout.str());
}
};
REGISTER_TEST(TestCondition)