/*
* Cppcheck - A tool for static C/C++ code analysis
* Copyright (C) 2007-2015 Daniel Marjamäki and 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 "tokenize.h"
#include "checkcondition.h"
#include "testsuite.h"
#include
#include
extern std::ostringstream errout;
class TestCondition : public TestFixture {
public:
TestCondition() : TestFixture("TestCondition") {
}
private:
void run() {
TEST_CASE(assignAndCompare); // assignment and comparison don't match
TEST_CASE(mismatchingBitAnd); // overlapping bitmasks
TEST_CASE(compare); // mismatching LHS/RHS in comparison
TEST_CASE(multicompare); // mismatching comparisons
TEST_CASE(duplicateIf); // duplicate 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(secondAlwaysTrueFalseWhenFirstTrueError);
TEST_CASE(incorrectLogicOp_condSwapping);
TEST_CASE(modulo);
TEST_CASE(oppositeInnerCondition);
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
}
void check(const char code[], bool validate=true, const char* filename = "test.cpp") {
// Clear the error buffer..
errout.str("");
Settings settings;
settings.addEnabled("style");
settings.addEnabled("warning");
CheckCondition checkCondition;
// Tokenize..
Tokenizer tokenizer(&settings, this);
std::istringstream istr(code);
tokenizer.tokenize(istr, filename);
checkCondition.runChecks(&tokenizer, &settings, this);
const std::string str1(tokenizer.tokens()->stringifyList(0,true));
tokenizer.simplifyTokenList2();
const std::string str2(tokenizer.tokens()->stringifyList(0,true));
checkCondition.runSimplifiedChecks(&tokenizer, &settings, this);
// Ensure that the test case is not bad.
if (validate && str1 != str2) {
warnUnsimplified(str1, str2);
}
}
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==1808' 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());
// 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"
"}\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 compare() {
check("void foo(int x)\n"
"{\n"
" if ((x & 4) == 3);\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (style) Expression '(X & 0x4) == 0x3' is always false.\n", errout.str());
check("void foo(int x)\n"
"{\n"
" if ((x | 4) == 3);\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (style) Expression '(X | 0x4) == 0x3' is always false.\n", errout.str());
check("void foo(int x)\n"
"{\n"
" if ((x | 4) != 3);\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (style) Expression '(X | 0x4) != 0x3' is always true.\n", errout.str());
check("void foo(int x)\n"
"{\n"
" if ((x & y & 4 & z ) == 3);\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (style) Expression '(X & 0x4) == 0x3' is always false.\n", errout.str());
}
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());
}
void checkPureFunction(const char code[]) {
// Clear the error buffer..
errout.str("");
const char cfg[] = "\n"
"\n"
" \n"
"";
tinyxml2::XMLDocument xmldoc;
xmldoc.Parse(cfg, sizeof(cfg));
Settings settings;
settings.addEnabled("style");
settings.addEnabled("warning");
settings.library.load(xmldoc);
// Tokenize..
Tokenizer tokenizer(&settings, this);
std::istringstream istr(code);
tokenizer.tokenize(istr, "test.cpp");
CheckCondition checkCondition;
checkCondition.runChecks(&tokenizer, &settings, this);
const std::string str1(tokenizer.tokens()->stringifyList(0,true));
tokenizer.simplifyTokenList2();
const std::string str2(tokenizer.tokens()->stringifyList(0,true));
checkCondition.runSimplifiedChecks(&tokenizer, &settings, this);
// Ensure that the test case is not bad.
if (str1 != str2) {
warnUnsimplified(str1, str2);
}
}
void duplicateIf() {
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(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