cppcheck/test/testvalueflow.cpp

344 lines
12 KiB
C++

/*
* Cppcheck - A tool for static C/C++ code analysis
* Copyright (C) 2007-2013 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 <http://www.gnu.org/licenses/>.
*/
#include "testsuite.h"
#include "testutils.h"
#include "valueflow.h"
#include "tokenize.h"
#include "token.h"
#include <vector>
#include <string>
extern std::ostringstream errout;
class TestValueFlow : public TestFixture {
public:
TestValueFlow() : TestFixture("TestValueFlow") {
}
private:
void run() {
TEST_CASE(valueFlowBeforeCondition);
TEST_CASE(valueFlowForLoop);
TEST_CASE(valueFlowSubFunction);
}
bool testValueOfX(const std::string &code, unsigned int linenr, int value) {
Settings settings;
settings.valueFlow = true; // temporary flag
// strcpy cfg
const char cfg[] = "<?xml version=\"1.0\"?>\n"
"<def>\n"
" <function name=\"strcpy\"> <arg nr=\"1\"><not-null/></arg> </function>\n"
"</def>";
settings.library.loadxmldata(cfg, sizeof(cfg));
// Tokenize..
Tokenizer tokenizer(&settings, this);
std::istringstream istr(code);
tokenizer.tokenize(istr, "test.cpp");
for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) {
if (tok->str() == "x" && tok->linenr() == linenr) {
std::list<ValueFlow::Value>::const_iterator it;
for (it = tok->values.begin(); it != tok->values.end(); ++it) {
if (it->intvalue == value)
return true;
}
}
}
return false;
}
void bailout(const char code[]) {
Settings settings;
settings.valueFlow = true; // temporary flag
settings.debugwarnings = true;
// Tokenize..
Tokenizer tokenizer(&settings, this);
std::istringstream istr(code);
errout.str("");
tokenizer.tokenize(istr, "test.cpp");
}
void valueFlowBeforeCondition() {
const char *code;
code = "void f(int x) {\n"
" int a = x;\n"
" if (x == 123) {}\n"
"}";
ASSERT_EQUALS(true, testValueOfX(code, 2U, 123));
code = "void f(unsigned int x) {\n"
" int a = x;\n"
" if (x >= 1) {}\n"
"}";
ASSERT_EQUALS(true, testValueOfX(code, 2U, 1));
ASSERT_EQUALS(true, testValueOfX(code, 2U, 0));
code = "void f(int *x) {\n"
" *x = 100;\n"
" if (x) {}\n"
"}";
ASSERT_EQUALS(true, testValueOfX(code, 2U, 0));
code = "extern const int x;\n"
"void f() {\n"
" int a = x;\n"
" if (x == 123) {}\n"
"}";
ASSERT_EQUALS(true, testValueOfX(code, 3U, 123));
// assignment
code = "void f(int x) {\n"
" x = 2 + x;\n"
" if (x == 65);\n"
"}";
ASSERT_EQUALS(false, testValueOfX(code, 2U, 65));
code = "void f(int x) {\n"
" x = y = 2 + x;\n"
" if (x == 65);\n"
"}";
ASSERT_EQUALS(false, testValueOfX(code, 2U, 65));
// guarding by &&
code = "void f(int x) {\n"
" if (!x || \n" // <- x can be 0
" a/x) {}\n" // <- x can't be 0
" if (x==0) {}\n"
"}";
ASSERT_EQUALS(true, testValueOfX(code, 2U, 0));
ASSERT_EQUALS(false, testValueOfX(code, 3U, 0));
code = "void f(int *x) {\n"
" ((x=ret())&&\n"
" (*x==0));\n" // <- x is not 0
" if (x==0) {}\n"
"}";
ASSERT_EQUALS(false, testValueOfX(code, 3U, 0));
// function calls
code = "void f(int x) {\n"
" a = x;\n"
" setx(x);\n"
" if (x == 1) {}\n"
"}";
ASSERT_EQUALS(true, testValueOfX(std::string("void setx(int x);")+code, 2U, 1));
ASSERT_EQUALS(false, testValueOfX(std::string("void setx(int &x);")+code, 2U, 1));
ASSERT_EQUALS(false, testValueOfX(code, 2U, 1));
code = "void f(char* x) {\n"
" strcpy(x,\"abc\");\n"
" if (x) {}\n"
"}";
ASSERT_EQUALS(true, testValueOfX(code, 2U, 0));
code = "void addNewFunction(Scope**scope, const Token**tok);\n"
"void f(Scope *x) {\n"
" x->functionList.back();\n"
" addNewFunction(&x,&tok);\n" // address-of, x can be changed by subfunction
" if (x) {}\n"
"}";
ASSERT_EQUALS(false, testValueOfX(code, 3U, 0));
// while, for, do-while
code = "void f(int x) {\n" // loop condition, x is not assigned inside loop => use condition
" a = x;\n" // x can be 37
" while (x == 37) {}\n"
"}";
ASSERT_EQUALS(true, testValueOfX(code, 2U, 37));
code = "void f(int x) {\n" // loop condition, x is assigned inside loop => dont use condition
" a = x;\n" // don't assume that x can be 37
" while (x != 37) { x++; }\n"
"}";
ASSERT_EQUALS(false, testValueOfX(code, 2U, 37));
code = "void f(int x) {\n" // condition inside loop, x is NOT assigned inside loop => use condition
" a = x;\n"
" do {\n"
" if (x==76) {}\n"
" } while (1);\n"
"}";
ASSERT_EQUALS(true, testValueOfX(code, 2U, 76));
code = "void f(int x) {\n" // conditions inside loop, x is assigned inside do-while => dont use condition
" a = x;\n"
" do {\n"
" if (x!=76) { x=do_something(); }\n"
" } while (1);\n"
"}";
ASSERT_EQUALS(false, testValueOfX(code, 2U, 76));
// bailout: ?:
bailout("void f(int x) {\n"
" y = ((x<0) ? x : ((x==2)?3:4));\n"
"}");
ASSERT_EQUALS("[test.cpp:2]: (debug) ValueFlow bailout: no simplification of x within ?: expression\n", errout.str());
bailout("int f(int x) {\n"
" int r = x ? 1 / x : 0;\n"
" if (x == 0) {}\n"
"}");
ASSERT_EQUALS("[test.cpp:2]: (debug) ValueFlow bailout: no simplification of x within ?: expression\n", errout.str());
code = "void f(int x) {\n"
" int a =v x;\n"
" a = b ? x/2 : 20/x;\n"
" if (x == 123) {}\n"
"}";
ASSERT_EQUALS(true, testValueOfX(code, 2U, 123));
code = "void f(int x, int y) {\n"
" a = x;\n"
" if (y){}\n"
" if (x==123){}\n"
"}";
ASSERT_EQUALS(true, testValueOfX(code, 2U, 123));
// skip sizeof
code = "void f(int *x) {\n"
" sizeof(x[0]);\n"
" if (x==63){}\n"
"}";
ASSERT_EQUALS(false, testValueOfX(code, 2U, 63));
// bailout: if/else/etc
bailout("void f(int x) {\n"
" if (x != 123) { b = x; }\n"
" if (x == 123) {}\n"
"}");
ASSERT_EQUALS("[test.cpp:2]: (debug) ValueFlow bailout: variable x stopping on }\n", errout.str());
// bailout: assignment
bailout("void f(int x) {\n"
" x = y;\n"
" if (x == 123) {}\n"
"}");
ASSERT_EQUALS("[test.cpp:2]: (debug) ValueFlow bailout: assignment of x\n", errout.str());
// bailout: global variables
bailout("int x;\n"
"void f() {\n"
" int a = x;\n"
" if (x == 123) {}\n"
"}");
ASSERT_EQUALS("[test.cpp:4]: (debug) ValueFlow bailout: global variable x\n", errout.str());
// bailout: switch
// TODO : handle switch/goto more intelligently
bailout("void f(int x, int y) {\n"
" switch (y) {\n"
" case 1: a=x; break;\n"
" case 2: if (x==5) {} break;\n"
" };\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (debug) ValueFlow bailout: variable x stopping on break\n", errout.str());
bailout("void f(int x, int y) {\n"
" switch (y) {\n"
" case 1: a=x; return 1;\n"
" case 2: if (x==5) {} break;\n"
" };\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (debug) ValueFlow bailout: variable x stopping on return\n", errout.str());
// bailout: condition is a expanded macro
bailout("void f(int x) {\n"
" a = x;\n"
" $if ($x==$123){}\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (debug) ValueFlow bailout: variable x, condition is defined in macro\n", errout.str());
// bailout: goto label (TODO: handle gotos more intelligently)
bailout("void f(int x) {\n"
" if (x == 123) { goto out; }\n"
" a=x;\n" // <- x is not 123
"out:"
" if (x==123){}\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (debug) ValueFlow bailout: variable x stopping on goto label\n", errout.str());
}
void valueFlowForLoop() {
const char *code;
code = "void f() {\n"
" for (int x = 0; x < 10; x++)\n"
" a[x] = 0;\n"
"}";
ASSERT_EQUALS(true, testValueOfX(code, 3U, 0));
ASSERT_EQUALS(true, testValueOfX(code, 3U, 9));
ASSERT_EQUALS(false, testValueOfX(code, 3U, 10));
code = "void f() {\n"
" for (int x = 0; x < ((short)10); x++)\n"
" a[x] = 0;\n"
"}";
ASSERT_EQUALS(true, testValueOfX(code, 3U, 0));
ASSERT_EQUALS(true, testValueOfX(code, 3U, 9));
ASSERT_EQUALS(false, testValueOfX(code, 3U, 10));
}
void valueFlowSubFunction() {
const char *code;
code = "void f1(int x) { return x; }\n"
"void f2(int x) {\n"
" f1(123);\n"
"}";
ASSERT_EQUALS(true, testValueOfX(code, 1U, 123));
code = "void f1(int x) { return x; }\n"
"void f2(int x) {\n"
" f1(x);\n"
" if (x==0){}\n"
"}";
ASSERT_EQUALS(true, testValueOfX(code, 1U, 0));
code = "void f1(int x) {\n"
" if (x == 0) return;\n"
" int y = 1234 / x;\n"
"}\n"
"\n"
"void f2() {\n"
" f1(0);\n"
"}";
ASSERT_EQUALS(false, testValueOfX(code, 3U, 0));
code = "void f1(int x) { a=x; }\n"
"void f2(int y) { f1(y<123); }\n";
ASSERT_EQUALS(true, testValueOfX(code, 1U, 0));
ASSERT_EQUALS(true, testValueOfX(code, 1U, 1));
code = "void f1(int x) { a=(abc)x; }\n"
"void f2(int y) { f1(123); }\n";
ASSERT_EQUALS(true, testValueOfX(code, 1U, 123));
}
};
REGISTER_TEST(TestValueFlow)