/*
* Cppcheck - A tool for static C/C++ code analysis
* Copyright (C) 2007-2023 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 "helpers.h"
#include "mathlib.h"
#include "platform.h"
#include "settings.h"
#include "fixture.h"
#include "token.h"
#include "tokenize.h"
#include "vfvalue.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
class TestValueFlow : public TestFixture {
public:
TestValueFlow() : TestFixture("TestValueFlow") {}
private:
Settings settings = settingsBuilder().library("std.cfg").build();
void run() override {
// strcpy, abort cfg
constexpr char cfg[] = "\n"
"\n"
" \n"
" true \n" // abort is a noreturn function
"";
settings = settingsBuilder(settings).libraryxml(cfg, sizeof(cfg)).build();
TEST_CASE(valueFlowNumber);
TEST_CASE(valueFlowString);
TEST_CASE(valueFlowPointerAlias);
TEST_CASE(valueFlowLifetime);
TEST_CASE(valueFlowArrayElement);
TEST_CASE(valueFlowMove);
TEST_CASE(valueFlowBitAnd);
TEST_CASE(valueFlowRightShift);
TEST_CASE(valueFlowCalculations);
TEST_CASE(valueFlowSizeof);
TEST_CASE(valueFlowComma);
TEST_CASE(valueFlowErrorPath);
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(valueFlowBeforeConditionForward);
TEST_CASE(valueFlowBeforeConditionConstructor);
TEST_CASE(valueFlowAfterAssign);
TEST_CASE(valueFlowAfterSwap);
TEST_CASE(valueFlowAfterCondition);
TEST_CASE(valueFlowAfterConditionTernary);
TEST_CASE(valueFlowAfterConditionExpr);
TEST_CASE(valueFlowAfterConditionSeveralNot);
TEST_CASE(valueFlowForwardCompoundAssign);
TEST_CASE(valueFlowForwardCorrelatedVariables);
TEST_CASE(valueFlowForwardModifiedVariables);
TEST_CASE(valueFlowForwardFunction);
TEST_CASE(valueFlowForwardTernary);
TEST_CASE(valueFlowForwardLambda);
TEST_CASE(valueFlowForwardTryCatch);
TEST_CASE(valueFlowForwardInconclusiveImpossible);
TEST_CASE(valueFlowForwardConst);
TEST_CASE(valueFlowForwardAfterCondition);
TEST_CASE(valueFlowFwdAnalysis);
TEST_CASE(valueFlowSwitchVariable);
TEST_CASE(valueFlowForLoop);
TEST_CASE(valueFlowSubFunction);
TEST_CASE(valueFlowFunctionReturn);
TEST_CASE(valueFlowFunctionDefaultParameter);
TEST_CASE(knownValue);
TEST_CASE(valueFlowSizeofForwardDeclaredEnum);
TEST_CASE(valueFlowGlobalVar);
TEST_CASE(valueFlowGlobalConstVar);
TEST_CASE(valueFlowGlobalStaticVar);
TEST_CASE(valueFlowInlineAssembly);
TEST_CASE(valueFlowSameExpression);
TEST_CASE(valueFlowUninit);
TEST_CASE(valueFlowConditionExpressions);
TEST_CASE(valueFlowContainerSize);
TEST_CASE(valueFlowContainerElement);
TEST_CASE(valueFlowDynamicBufferSize);
TEST_CASE(valueFlowSafeFunctionParameterValues);
TEST_CASE(valueFlowUnknownFunctionReturn);
TEST_CASE(valueFlowPointerAliasDeref);
TEST_CASE(valueFlowCrashIncompleteCode);
TEST_CASE(valueFlowCrash);
TEST_CASE(valueFlowHang);
TEST_CASE(valueFlowCrashConstructorInitialization);
TEST_CASE(valueFlowUnknownMixedOperators);
TEST_CASE(valueFlowSolveExpr);
TEST_CASE(valueFlowIdempotent);
TEST_CASE(valueFlowUnsigned);
TEST_CASE(valueFlowMod);
TEST_CASE(valueFlowNotNull);
TEST_CASE(valueFlowSymbolic);
TEST_CASE(valueFlowSymbolicIdentity);
TEST_CASE(valueFlowSymbolicStrlen);
TEST_CASE(valueFlowSmartPointer);
TEST_CASE(valueFlowImpossibleMinMax);
TEST_CASE(valueFlowImpossibleIncDec);
TEST_CASE(valueFlowImpossibleUnknownConstant);
TEST_CASE(valueFlowContainerEqual);
TEST_CASE(performanceIfCount);
}
static bool isNotTokValue(const ValueFlow::Value &val) {
return !val.isTokValue();
}
// cppcheck-suppress unusedPrivateFunction
static bool isNotLifetimeValue(const ValueFlow::Value& val) {
return !val.isLifetimeValue();
}
static bool isNotUninitValue(const ValueFlow::Value& val) {
return !val.isUninitValue();
}
static bool isNotPossible(const ValueFlow::Value& val) {
return !val.isPossible();
}
static bool isNotKnown(const ValueFlow::Value& val) {
return !val.isKnown();
}
static bool isNotInconclusive(const ValueFlow::Value& val) {
return !val.isInconclusive();
}
static bool isNotImpossible(const ValueFlow::Value& val) {
return !val.isImpossible();
}
#define testValueOfXKnown(...) testValueOfXKnown_(__FILE__, __LINE__, __VA_ARGS__)
bool testValueOfXKnown_(const char* file, int line, const char code[], unsigned int linenr, int value) {
// Tokenize..
Tokenizer tokenizer(&settings, this);
std::istringstream istr(code);
ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line);
for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) {
if (tok->str() == "x" && tok->linenr() == linenr) {
if (std::any_of(tok->values().begin(), tok->values().end(), [&](const ValueFlow::Value& val) {
if (val.isSymbolicValue())
return false;
if (val.isKnown() && val.intvalue == value)
return true;
return false;
}))
return true;
}
}
return false;
}
bool testValueOfXKnown_(const char* file, int line, const char code[], unsigned int linenr, const std::string& expr, int value) {
// Tokenize..
Tokenizer tokenizer(&settings, this);
std::istringstream istr(code);
ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line);
for (const Token* tok = tokenizer.tokens(); tok; tok = tok->next()) {
if (tok->str() == "x" && tok->linenr() == linenr) {
if (std::any_of(tok->values().begin(), tok->values().end(), [&](const ValueFlow::Value& val) {
if (!val.isSymbolicValue())
return false;
if (val.isKnown() && val.intvalue == value && val.tokvalue->expressionString() == expr)
return true;
return false;
}))
return true;
}
}
return false;
}
#define testValueOfXImpossible(...) testValueOfXImpossible_(__FILE__, __LINE__, __VA_ARGS__)
bool testValueOfXImpossible_(const char* file, int line, const char code[], unsigned int linenr, int value) {
// Tokenize..
Tokenizer tokenizer(&settings, this);
std::istringstream istr(code);
ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line);
for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) {
if (tok->str() == "x" && tok->linenr() == linenr) {
if (std::any_of(tok->values().begin(), tok->values().end(), [&](const ValueFlow::Value& val) {
if (val.isSymbolicValue())
return false;
if (val.isImpossible() && val.intvalue == value)
return true;
return false;
}))
return true;
}
}
return false;
}
bool testValueOfXImpossible_(const char* file, int line, const char code[], unsigned int linenr, const std::string& expr, int value)
{
// Tokenize..
Tokenizer tokenizer(&settings, this);
std::istringstream istr(code);
ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line);
for (const Token* tok = tokenizer.tokens(); tok; tok = tok->next()) {
if (tok->str() == "x" && tok->linenr() == linenr) {
if (std::any_of(tok->values().begin(), tok->values().end(), [&](const ValueFlow::Value& val) {
if (!val.isSymbolicValue())
return false;
if (val.isImpossible() && val.intvalue == value && val.tokvalue->expressionString() == expr)
return true;
return false;
}))
return true;
}
}
return false;
}
#define testValueOfXInconclusive(code, linenr, value) testValueOfXInconclusive_(code, linenr, value, __FILE__, __LINE__)
bool testValueOfXInconclusive_(const char code[], unsigned int linenr, int value, const char* file, int line) {
// Tokenize..
Tokenizer tokenizer(&settings, this);
std::istringstream istr(code);
ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line);
for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) {
if (tok->str() == "x" && tok->linenr() == linenr) {
if (std::any_of(tok->values().begin(), tok->values().end(), [&](const ValueFlow::Value& val) {
if (val.isSymbolicValue())
return false;
if (val.isInconclusive() && val.intvalue == value)
return true;
return false;
}))
return true;
}
}
return false;
}
#define testValueOfX(...) testValueOfX_(__FILE__, __LINE__, __VA_ARGS__)
bool testValueOfX_(const char* file, int line, const char code[], unsigned int linenr, int value, const Settings *s = nullptr) {
const Settings *settings1 = s ? s : &settings;
// Tokenize..
Tokenizer tokenizer(settings1, this);
std::istringstream istr(code);
ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line);
for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) {
if (tok->str() == "x" && tok->linenr() == linenr) {
if (std::any_of(tok->values().cbegin(), tok->values().cend(), [&](const ValueFlow::Value& v) {
return v.isIntValue() && !v.isImpossible() && v.intvalue == value;
}))
return true;
}
}
return false;
}
bool testValueOfX_(const char* file, int line, const char code[], unsigned int linenr, const std::string& expr, int value)
{
// Tokenize..
Tokenizer tokenizer(&settings, this);
std::istringstream istr(code);
ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line);
for (const Token* tok = tokenizer.tokens(); tok; tok = tok->next()) {
if (tok->str() == "x" && tok->linenr() == linenr) {
if (std::any_of(tok->values().cbegin(), tok->values().cend(), [&](const ValueFlow::Value& v) {
return v.isSymbolicValue() && !v.isImpossible() && v.intvalue == value && v.tokvalue->expressionString() == expr;
}))
return true;
}
}
return false;
}
bool testValueOfX_(const char* file, int line, const char code[], unsigned int linenr, double value, double diff) {
// Tokenize..
Tokenizer tokenizer(&settings, this);
std::istringstream istr(code);
ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line);
for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) {
if (tok->str() == "x" && tok->linenr() == linenr) {
if (std::any_of(tok->values().cbegin(), tok->values().cend(), [&](const ValueFlow::Value& v) {
return v.isFloatValue() && !v.isImpossible() && v.floatValue >= value - diff && v.floatValue next()) {
if (tok->str() != "x" || tok->linenr() != linenr)
continue;
std::ostringstream ostr;
for (const ValueFlow::Value &v : tok->values()) {
for (const ValueFlow::Value::ErrorPathItem &ep : v.errorPath) {
const Token *eptok = ep.first;
const std::string &msg = ep.second;
ostr linenr() linenr() == linenr) {
if (std::any_of(tok->values().cbegin(), tok->values().cend(), [&](const ValueFlow::Value& v) {
return v.valueType == type && Token::simpleMatch(v.tokvalue, value, len);
}))
return true;
}
}
return false;
}
#define testLifetimeOfX(...) testLifetimeOfX_(__FILE__, __LINE__, __VA_ARGS__)
bool testLifetimeOfX_(const char* file, int line, const char code[], unsigned int linenr, const char value[], ValueFlow::Value::LifetimeScope lifetimeScope = ValueFlow::Value::LifetimeScope::Local) {
// Tokenize..
Tokenizer tokenizer(&settings, this);
std::istringstream istr(code);
ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line);
const std::size_t len = strlen(value);
for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) {
if (tok->str() == "x" && tok->linenr() == linenr) {
if (std::any_of(tok->values().cbegin(), tok->values().cend(), [&](const ValueFlow::Value& v) {
return v.isLifetimeValue() && v.lifetimeScope == lifetimeScope && Token::simpleMatch(v.tokvalue, value, len);
}))
return true;
}
}
return false;
}
bool testValueOfX_(const char* file, int line, const char code[], unsigned int linenr, int value, ValueFlow::Value::ValueType type) {
// Tokenize..
Tokenizer tokenizer(&settings, this);
std::istringstream istr(code);
ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line);
for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) {
if (tok->str() == "x" && tok->linenr() == linenr) {
if (std::any_of(tok->values().cbegin(), tok->values().cend(), [&](const ValueFlow::Value& v) {
return v.valueType == type && v.intvalue == value;
}))
return true;
}
}
return false;
}
bool testValueOfX_(const char* file, int line, const char code[], unsigned int linenr, ValueFlow::Value::MoveKind moveKind) {
// Tokenize..
Tokenizer tokenizer(&settings, this);
std::istringstream istr(code);
ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line);
for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) {
if (tok->str() == "x" && tok->linenr() == linenr) {
if (std::any_of(tok->values().cbegin(), tok->values().cend(), [&](const ValueFlow::Value& v) {
return v.isMovedValue() && v.moveKind == moveKind;
}))
return true;
}
}
return false;
}
#define testConditionalValueOfX(code, linenr, value) testConditionalValueOfX_(code, linenr, value, __FILE__, __LINE__)
bool testConditionalValueOfX_(const char code[], unsigned int linenr, int value, const char* file, int line) {
// Tokenize..
Tokenizer tokenizer(&settings, this);
std::istringstream istr(code);
ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line);
for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) {
if (tok->str() == "x" && tok->linenr() == linenr) {
if (std::any_of(tok->values().cbegin(), tok->values().cend(), [&](const ValueFlow::Value& v) {
return v.isIntValue() && v.intvalue == value && v.condition;
}))
return true;
}
}
return false;
}
#define bailout(...) bailout_(__FILE__, __LINE__, __VA_ARGS__)
void bailout_(const char* file, int line, const char code[]) {
const Settings s = settingsBuilder().debugwarnings().build();
errout.str("");
std::vector files(1, "test.cpp");
Tokenizer tokenizer(&s, this);
PreprocessorHelper::preprocess(code, files, tokenizer);
// Tokenize..
ASSERT_LOC(tokenizer.simplifyTokens1(""), file, line);
}
#define tokenValues(...) tokenValues_(__FILE__, __LINE__, __VA_ARGS__)
std::list tokenValues_(const char* file, int line, const char code[], const char tokstr[], const Settings *s = nullptr) {
Tokenizer tokenizer(s ? s : &settings, this);
std::istringstream istr(code);
errout.str("");
ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line);
const Token *tok = Token::findmatch(tokenizer.tokens(), tokstr);
return tok ? tok->values() : std::list();
}
std::list tokenValues_(const char* file, int line, const char code[], const char tokstr[], ValueFlow::Value::ValueType vt, const Settings *s = nullptr) {
std::list values = tokenValues_(file, line, code, tokstr, s);
values.remove_if([&](const ValueFlow::Value& v) {
return v.valueType != vt;
});
return values;
}
#define lifetimeValues(...) lifetimeValues_(__FILE__, __LINE__, __VA_ARGS__)
std::vector lifetimeValues_(const char* file, int line, const char code[], const char tokstr[], const Settings *s = nullptr) {
std::vector result;
Tokenizer tokenizer(s ? s : &settings, this);
std::istringstream istr(code);
errout.str("");
ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line);
const Token *tok = Token::findmatch(tokenizer.tokens(), tokstr);
if (!tok)
return result;
for (const ValueFlow::Value& value:tok->values()) {
if (!value.isLifetimeValue())
continue;
if (!value.tokvalue)
continue;
result.push_back(value.tokvalue->expressionString());
}
return result;
}
#define valueOfTok(code, tokstr) valueOfTok_(code, tokstr, __FILE__, __LINE__)
ValueFlow::Value valueOfTok_(const char code[], const char tokstr[], const char* file, int line) {
std::list values = removeImpossible(tokenValues_(file, line, code, tokstr));
return values.size() == 1U && !values.front().isTokValue() ? values.front() : ValueFlow::Value();
}
static std::list removeSymbolicTok(std::list values)
{
values.remove_if([](const ValueFlow::Value& v) {
return v.isSymbolicValue() || v.isTokValue();
});
return values;
}
static std::list removeImpossible(std::list values)
{
values.remove_if(std::mem_fn(&ValueFlow::Value::isImpossible));
return values;
}
void valueFlowNumber() {
ASSERT_EQUALS(123, valueOfTok("x=123;", "123").intvalue);
ASSERT_EQUALS_DOUBLE(192.0, valueOfTok("x=0x0.3p10;", "0x0.3p10").floatValue, 1e-5); // 3 * 16^-1 * 2^10 = 192
ASSERT(std::fabs(valueOfTok("x=0.5;", "0.5").floatValue - 0.5) < 0.1);
ASSERT_EQUALS(10, valueOfTok("enum {A=10,B=15}; x=A+0;", "+").intvalue);
ASSERT_EQUALS(0, valueOfTok("x=false;", "false").intvalue);
ASSERT_EQUALS(1, valueOfTok("x=true;", "true").intvalue);
ASSERT_EQUALS(0, valueOfTok("x(NULL);", "NULL").intvalue);
ASSERT_EQUALS((int)('a'), valueOfTok("x='a';", "'a'").intvalue);
ASSERT_EQUALS((int)('\n'), valueOfTok("x='\\n';", "'\\n'").intvalue);
TODO_ASSERT_EQUALS(0xFFFFFFFF00000000, 0, valueOfTok("x=0xFFFFFFFF00000000;", "0xFFFFFFFF00000000").intvalue); // #7701
ASSERT_EQUALS_DOUBLE(16, valueOfTok("x=(double)16;", "(").floatValue, 1e-5);
ASSERT_EQUALS_DOUBLE(0.0625, valueOfTok("x=1/(double)16;", "/").floatValue, 1e-5);
// scope
{
const char code[] = "namespace N { enum E {e0,e1}; }\n"
"void foo() { x = N::e1; }";
ASSERT_EQUALS(1, valueOfTok(code, "::").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\"", ValueFlow::Value::ValueType::TOK));
// valueFlowSubFunction
code = "void dostuff(const char *x) {\n"
" f(x);\n"
"}\n"
"\n"
"void test() { dostuff(\"abc\"); }";
ASSERT_EQUALS(true, testValueOfX(code, 2, "\"abc\"", ValueFlow::Value::ValueType::TOK));
}
void valueFlowPointerAlias() {
const char *code;
std::list values;
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 ]", ValueFlow::Value::ValueType::TOK));
// dead pointer
code = "void f() {\n"
" int *x;\n"
" if (cond) { int i; x = &i; }\n"
" *x = 0;\n" // 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));
// after loop
code = "void f(struct X *x) {\n"
" do {\n"
" if (!x)\n"
" break;\n"
" } while (x->a);\n"
" if (x) {}\n"
"}\n";
ASSERT_EQUALS(false, testValueOfX(code, 5U, 0));
ASSERT_EQUALS(false, testValueOfXKnown(code, 3U, 1));
TODO_ASSERT_EQUALS(true, false, testValueOfX(code, 6U, 0));
}
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));
// compound assignment += , -= , ...
code = "void f(int x) {\n"
" a = x;\n"
" x += 2;\n"
" if (x == 4);\n"
"}";
ASSERT_EQUALS(true, testValueOfX(code, 2U, 2));
ASSERT_EQUALS(true, testValueOfX(code, 3U, 2));
code = "void f(int x) {\n"
" a = x;\n"
" x -= 2;\n"
" if (x == 4);\n"
"}";
ASSERT_EQUALS(true, testValueOfX(code, 2U, 6));
ASSERT_EQUALS(true, testValueOfX(code, 3U, 6));
code = "void f(int x) {\n"
" a = x;\n"
" x *= 2;\n"
" if (x == 42);\n"
"}";
ASSERT_EQUALS(true, testValueOfX(code, 2U, 21));
ASSERT_EQUALS(true, testValueOfX(code, 3U, 21));
code = "void f(int x) {\n"
" a = x;\n"
" x /= 5;\n"
" if (x == 42);\n"
"}";
ASSERT_EQUALS(true, testValueOfX(code, 2U, 210));
ASSERT_EQUALS(true, testValueOfX(code, 3U, 210));
// bailout: assignment
bailout("void f(int x) {\n"
" x = y;\n"
" if (x == 123) {}\n"
"}");
ASSERT_EQUALS(
"[test.cpp:2]: (debug) valueFlowConditionExpressions bailout: Skipping function due to incomplete variable y\n",
errout.str());
}
void valueFlowBeforeConditionAndAndOrOrGuard() { // guarding by &&
const char *code;
code = "void f(int x) {\n"
" if (!x || \n" // don't use condition
" a = x;\n"
" for (i=1;ia : 0) {}\n"
"}";
ASSERT_EQUALS(true, testValueOfX(code, 2U, 0));
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));
}
void valueFlowBeforeConditionSizeof() { // skip sizeof
const char *code;
code = "void f(int *x) {\n"
" sizeof(x[0]);\n"
" if (x==63){}\n"
"}";
ASSERT_EQUALS(false, testValueOfX(code, 2U, 63));
code = "void f(int *x) {\n"
" char a[sizeof x.y];\n"
" if (x==0){}\n"
"}";
ASSERT_EQUALS(false, testValueOfX(code, 2U, 0));
}
void valueFlowBeforeConditionIfElse() { // bailout: if/else/etc
const char *code;
code = "void f(X * x) {\n"
" a = x;\n"
" if ((x != NULL) &&\n"
" (a(x->name, html)) &&\n"
" (a(x->name, 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) valueFlowConditionExpressions bailout: Skipping function due to incomplete variable b\n",
errout.str());
code = "void f(int x, bool abc) {\n"
" a = x;\n"
" if (abc) { x = 1; }\n" //