/*
* Cppcheck - A tool for static C/C++ code analysis
* Copyright (C) 2007-2014 Daniel Marjamki 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 "testsuite.h"
#include "testutils.h"
#include "valueflow.h"
#include "tokenize.h"
#include "token.h"
#include
#include
extern std::ostringstream errout;
class TestValueFlow : public TestFixture {
public:
TestValueFlow() : TestFixture("TestValueFlow") {
}
private:
void run() {
TEST_CASE(valueFlowNumber);
TEST_CASE(valueFlowString);
TEST_CASE(valueFlowPointerAlias);
TEST_CASE(valueFlowBitAnd);
TEST_CASE(valueFlowCalculations);
TEST_CASE(valueFlowBeforeCondition);
TEST_CASE(valueFlowBeforeConditionAndAndOrOrGuard);
TEST_CASE(valueFlowBeforeConditionAssignIncDec);
TEST_CASE(valueFlowBeforeConditionFunctionCall);
TEST_CASE(valueFlowBeforeConditionGlobalVariables);
TEST_CASE(valueFlowBeforeConditionGoto);
TEST_CASE(valueFlowBeforeConditionIfElse);
TEST_CASE(valueFlowBeforeConditionLoop);
TEST_CASE(valueFlowBeforeConditionMacro);
TEST_CASE(valueFlowBeforeConditionSizeof);
TEST_CASE(valueFlowBeforeConditionSwitch);
TEST_CASE(valueFlowBeforeConditionTernaryOp);
TEST_CASE(valueFlowAfterAssign);
TEST_CASE(valueFlowAfterCondition);
TEST_CASE(valueFlowForLoop);
TEST_CASE(valueFlowSubFunction);
TEST_CASE(valueFlowFunctionReturn);
}
bool testValueOfX(const char code[], unsigned int linenr, int value) {
Settings settings;
// strcpy cfg
const char cfg[] = "\n"
"\n"
" \n"
"";
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::const_iterator it;
for (it = tok->values.begin(); it != tok->values.end(); ++it) {
if (it->intvalue == value && !it->tokvalue)
return true;
}
}
}
return false;
}
bool testValueOfX(const char code[], unsigned int linenr, const char value[]) {
Settings settings;
// strcpy cfg
const char cfg[] = "\n"
"\n"
" \n"
"";
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::const_iterator it;
for (it = tok->values.begin(); it != tok->values.end(); ++it) {
if (Token::simpleMatch(it->tokvalue, value))
return true;
}
}
}
return false;
}
void bailout(const char code[]) {
Settings settings;
settings.debugwarnings = true;
// Tokenize..
Tokenizer tokenizer(&settings, this);
std::istringstream istr(code);
errout.str("");
tokenizer.tokenize(istr, "test.cpp");
}
std::list tokenValues(const char code[], const char tokstr[]) {
const Settings settings;
Tokenizer tokenizer(&settings, this);
std::istringstream istr(code);
errout.str("");
tokenizer.tokenize(istr, "test.cpp");
const Token *tok = Token::findmatch(tokenizer.tokens(), tokstr);
return tok ? tok->values : std::list();
}
ValueFlow::Value valueOfTok(const char code[], const char tokstr[]) {
std::list values = tokenValues(code, tokstr);
return values.size() == 1U && !values.front().tokvalue ? values.front() : ValueFlow::Value();
}
void valueFlowNumber() {
const char *code;
code = "void f() {\n"
" x = 123;\n"
"}";
ASSERT_EQUALS(123, valueOfTok(code, "123").intvalue);
}
void valueFlowString() {
const char *code;
// valueFlowAfterAssign
code = "const char * f() {\n"
" static const char *x;\n"
" if (a) x = \"123\";\n"
" return x;\n"
"}";
ASSERT_EQUALS(true, testValueOfX(code, 4, "\"123\""));
// valueFlowSubFunction
code = "void dostuff(const char *x) {\n"
" f(x);\n"
"}\n"
"\n"
"void test() { dostuff(\"abc\"); }";
ASSERT_EQUALS(true, testValueOfX(code, 2, "\"abc\""));
}
void valueFlowPointerAlias() {
const char *code;
code = "const char * f() {\n"
" static const char *x;\n"
" static char ret[10];\n"
" if (a) x = &ret[0];\n"
" return x;\n"
"}";
ASSERT_EQUALS(true, testValueOfX(code, 5, "& ret [ 0 ]"));
// dead pointer
code = "void f() {\n"
" int *x;\n"
" if (cond) { int i; x = &i; }\n"
" *x = 0;\n" // calculation
code = "void f(int x) {\n"
" a = x + 8;\n"
"}\n"
"void callf() {\n"
" f(7);\n"
"}";
ASSERT_EQUALS(15, valueOfTok(code, "+").intvalue);
code = "void f(int x, int y) {\n"
" a = x + y;\n"
"}\n"
"void callf() {\n"
" f(1,1);\n"
" f(10,10);\n"
"}";
values = tokenValues(code, "+");
ASSERT_EQUALS(true, values.empty());
if (!values.empty()) {
/* todo.. */
ASSERT_EQUALS(2U, values.size());
ASSERT_EQUALS(2, values.front().intvalue);
ASSERT_EQUALS(22, values.back().intvalue);
}
}
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(unsigned int x) {\n"
" int a = x;\n"
" if (x > 0) {}\n"
"}";
ASSERT_EQUALS(true, testValueOfX(code, 2U, 0));
code = "void f(unsigned int x) {\n"
" int a = x;\n"
" if (x > 1) {}\n" // not zero => don't consider > condition
"}";
ASSERT_EQUALS(false, testValueOfX(code, 2U, 1));
code = "void f(int x) {\n" // not unsigned => don't consider > condition
" int a = x;\n"
" if (x > 0) {}\n"
"}";
ASSERT_EQUALS(false, 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));
}
void valueFlowBeforeConditionAssignIncDec() { // assignment / increment
const char *code;
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));
code = "void f(int x) {\n"
" a[x++] = 0;\n"
" if (x == 5);\n"
"}";
ASSERT_EQUALS(false, testValueOfX(code, 2U, 5));
code = "void f(int x) {\n"
" a = x;\n"
" x++;\n"
" if (x == 4);\n"
"}";
ASSERT_EQUALS(true, testValueOfX(code, 2U, 3));
// 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());
}
void valueFlowBeforeConditionAndAndOrOrGuard() { // guarding by &&
const char *code;
code = "void f(int x) {\n"
" if (!x || \n" // 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));
code = "void f(X x) {\n" // conditions inside loop, x is assigned inside do-while => dont use condition
" a = x;\n"
" for (i=1;iname, body))) {}\n"
" if (x != NULL) { }\n"
"}";
ASSERT_EQUALS(true, testValueOfX(code, 2U, 0));
ASSERT_EQUALS(true, testValueOfX(code, 3U, 0));
ASSERT_EQUALS(false, testValueOfX(code, 4U, 0));
ASSERT_EQUALS(false, testValueOfX(code, 5U, 0));
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());
code = "void f(int x) {\n"
" a = x;\n"
" if (abc) { x = 1; }\n" //