/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2020 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 "library.h" #include "platform.h" #include "settings.h" #include "testsuite.h" #include "token.h" #include "tokenize.h" #include "valueflow.h" #include #include #include #include #include #include #include #include #include #include #include class TestValueFlow : public TestFixture { public: TestValueFlow() : TestFixture("TestValueFlow") { } private: Settings settings; void run() OVERRIDE { // strcpy, abort cfg const char cfg[] = "\n" "\n" " \n" " true \n" // abort is a noreturn function ""; settings.library.loadxmldata(cfg, sizeof(cfg)); LOAD_LIB_2(settings.library, "std.cfg"); 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(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(valueFlowAfterAssign); TEST_CASE(valueFlowAfterCondition); 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(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(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(valueFlowIdempotent); } static bool isNotTokValue(const ValueFlow::Value &val) { return !val.isTokValue(); } static bool isNotLifetimeValue(const ValueFlow::Value& val) { return !val.isLifetimeValue(); } bool testValueOfXKnown(const char code[], unsigned int linenr, int value) { // 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) { for (const ValueFlow::Value& val:tok->values()) { if (val.isKnown() && val.intvalue == value) return true; } } } return false; } bool testValueOfXImpossible(const char code[], unsigned int linenr, int value) { // 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) { for (const ValueFlow::Value& val:tok->values()) { if (val.isImpossible() && val.intvalue == value) return true; } } } return false; } bool testValueOfXInconclusive(const char code[], unsigned int linenr, int value) { // 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) { for (const ValueFlow::Value& val:tok->values()) { if (val.isInconclusive() && val.intvalue == value) return true; } } } return false; } bool testValueOfX(const char code[], unsigned int linenr, int value) { // 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) { for (const ValueFlow::Value &v : tok->values()) { if (v.isIntValue() && !v.isImpossible() && v.intvalue == value) return true; } } } return false; } bool testValueOfX(const char code[], unsigned int linenr, float value, float diff) { // 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) { for (const ValueFlow::Value &v : tok->values()) { if (v.isFloatValue() && !v.isImpossible() && v.floatValue >= value - diff && v.floatValue <= value + diff) return true; } } } return false; } std::string getErrorPathForX(const char code[], unsigned int linenr) { // 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) 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 << eptok->linenr() << ',' << msg << '\n'; } } return ostr.str(); } return ""; } bool testValueOfX(const char code[], unsigned int linenr, const char value[], ValueFlow::Value::ValueType type) { // 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) { for (const ValueFlow::Value &v : tok->values()) { if (v.valueType == type && Token::simpleMatch(v.tokvalue, value, strlen(value))) return true; } } } return false; } bool testValueOfX(const char code[], unsigned int linenr, int value, ValueFlow::Value::ValueType type) { // 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) { for (const ValueFlow::Value &v : tok->values()) { if (v.valueType == type && v.intvalue == value) return true; } } } return false; } bool testValueOfX(const char code[], unsigned int linenr, ValueFlow::Value::MoveKind moveKind) { // 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) { for (const ValueFlow::Value &v : tok->values()) { if (v.isMovedValue() && v.moveKind == moveKind) return true; } } } return false; } bool testConditionalValueOfX(const char code[], unsigned int linenr, int value) { // 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) { for (const ValueFlow::Value &v : tok->values()) { if (v.isIntValue() && v.intvalue == value && v.condition) return true; } } } return false; } void bailout(const char code[]) { settings.debugwarnings = true; errout.str(""); std::vector files(1, "test.cpp"); std::istringstream istr(code); const simplecpp::TokenList tokens1(istr, files, files[0]); simplecpp::TokenList tokens2(files); std::map filedata; simplecpp::preprocess(tokens2, tokens1, files, filedata, simplecpp::DUI()); // Tokenize.. Tokenizer tokenizer(&settings, this); tokenizer.createTokens(std::move(tokens2)); tokenizer.simplifyTokens1(""); settings.debugwarnings = false; } std::list tokenValues(const char code[], const char tokstr[], const Settings *s = nullptr) { Tokenizer tokenizer(s ? s : &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(); } std::list tokenValues(const char code[], const char tokstr[], ValueFlow::Value::ValueType vt, const Settings *s = nullptr) { std::list values = tokenValues(code, tokstr, s); values.remove_if([&](const ValueFlow::Value& v) { return v.valueType != vt; }); return values; } std::vector lifetimeValues(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(""); tokenizer.tokenize(istr, "test.cpp"); 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; } ValueFlow::Value valueOfTok(const char code[], const char tokstr[]) { std::list values = tokenValues(code, tokstr); return values.size() == 1U && !values.front().isTokValue() ? values.front() : ValueFlow::Value(); } 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.5f) < 0.1f); 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); ASSERT_EQUALS(0xFFFFFFFF00000000, valueOfTok("x=0xFFFFFFFF00000000;","0xFFFFFFFF00000000").intvalue); // #7701 // 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" // <- x can point at i "}"; ASSERT_EQUALS(true, testValueOfX(code, 4, "& i", ValueFlow::Value::ValueType::TOK)); code = "void f() {\n" " struct X *x;\n" " x = &x[1];\n" "}"; values = tokenValues(code, "&"); values.remove_if(&isNotTokValue); ASSERT_EQUALS(true, values.empty()); values = tokenValues(code, "x ["); values.remove_if(&isNotTokValue); ASSERT_EQUALS(true, values.empty()); } void valueFlowLifetime() { const char *code; std::vector lifetimes; LOAD_LIB_2(settings.library, "std.cfg"); code = "void f() {\n" " int a = 1;\n" " auto x = [&]() { return a + 1; };\n" " auto b = x;\n" "}\n"; ASSERT_EQUALS(true, testValueOfX(code, 4, "a + 1", ValueFlow::Value::ValueType::LIFETIME)); code = "void f() {\n" " int a = 1;\n" " auto x = [=]() { return a + 1; };\n" " auto b = x;\n" "}\n"; ASSERT_EQUALS(false, testValueOfX(code, 4, "a ;", ValueFlow::Value::ValueType::LIFETIME)); code = "void f(int v) {\n" " int a = v;\n" " int * p = &a;\n" " auto x = [=]() { return p + 1; };\n" " auto b = x;\n" "}\n"; ASSERT_EQUALS(true, testValueOfX(code, 5, "a ;", ValueFlow::Value::ValueType::LIFETIME)); code = "void f() {\n" " std::vector v;\n" " auto x = v.begin();\n" " auto it = x;\n" "}\n"; ASSERT_EQUALS(true, testValueOfX(code, 4, "v . begin", ValueFlow::Value::ValueType::LIFETIME)); code = "void f() {\n" " int i = 0;\n" " void* x = (void*)&i;\n" "}\n"; lifetimes = lifetimeValues(code, "( void * )"); ASSERT_EQUALS(true, lifetimes.size() == 1); ASSERT_EQUALS(true, lifetimes.front() == "i"); } void valueFlowArrayElement() { const char *code; code = "void f() {\n" " const int x[] = {43,23,12};\n" " return x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, "{ 43 , 23 , 12 }", ValueFlow::Value::ValueType::TOK)); code = "void f() {\n" " const char x[] = \"abcd\";\n" " return x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, "\"abcd\"", ValueFlow::Value::ValueType::TOK)); code = "void f() {\n" " char x[32] = \"abcd\";\n" " return x;\n" "}"; TODO_ASSERT_EQUALS(true, false, testValueOfX(code, 3U, "\"abcd\"", ValueFlow::Value::ValueType::TOK)); code = "void f() {\n" " int a[10];\n" " int *x = a;\n" // <- a value is a " *x = 0;\n" // .. => x value is a "}"; ASSERT_EQUALS(true, testValueOfX(code, 4, "a", ValueFlow::Value::ValueType::TOK)); code = "char f() {\n" " const char *x = \"abcd\";\n" " return x[0];\n" "}"; ASSERT_EQUALS((int)('a'), valueOfTok(code, "[").intvalue); code = "char f() {\n" " const char *x = \"\";\n" " return x[0];\n" "}"; ASSERT_EQUALS(0, valueOfTok(code, "[").intvalue); } void valueFlowMove() { const char *code; code = "void f() {\n" " X x;\n" " g(std::move(x));\n" " y=x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, ValueFlow::Value::MoveKind::MovedVariable)); code = "void f() {\n" " X x;\n" " g(std::forward(x));\n" " y=x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, ValueFlow::Value::MoveKind::ForwardedVariable)); code = "void f() {\n" " X x;\n" " g(std::move(x).getA());\n" // Only parts of x might be moved out " y=x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, ValueFlow::Value::MoveKind::MovedVariable)); code = "void f() {\n" " X x;\n" " g(std::forward(x).getA());\n" // Only parts of x might be moved out " y=x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, ValueFlow::Value::MoveKind::ForwardedVariable)); code = "void f() {\n" " X x;\n" " g(std::move(x));\n" " x.clear();\n" " y=x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 5U, ValueFlow::Value::MoveKind::MovedVariable)); code = "void f() {\n" " X x;\n" " g(std::move(x));\n" " y=x->y;\n" " z=x->z;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 5U, ValueFlow::Value::MoveKind::MovedVariable)); code = "void f(int i) {\n" " X x;\n" " z = g(std::move(x));\n" " y = x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, ValueFlow::Value::MoveKind::MovedVariable)); code = "void f(int i) {\n" " X x;\n" " y = g(std::move(x), \n" " x.size());\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, ValueFlow::Value::MoveKind::MovedVariable)); code = "void f(int i) {\n" " X x;\n" " x = g(std::move(x));\n" " y = x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, ValueFlow::Value::MoveKind::MovedVariable)); code = "A f(int i) {\n" " X x;\n" " if (i)" " return g(std::move(x));\n" " return h(std::move(x));\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 5U, ValueFlow::Value::MoveKind::MovedVariable)); code = "struct X {\n" "};\n" "struct Data {\n" " template\n" " void foo(Fun f) {}\n" "};\n" "Data g(X value) { return Data(); }\n" "void f() {\n" " X x;\n" " g(std::move(x)).foo([=](int value) mutable {;});\n" " X y=x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 11U, ValueFlow::Value::MoveKind::MovedVariable)); code = "void f(int x) {\n" " g(std::move(x));\n" " y=x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 3U, ValueFlow::Value::MoveKind::MovedVariable)); } void valueFlowCalculations() { const char *code; // Different operators ASSERT_EQUALS(5, valueOfTok("3 + (a ? b : 2);", "+").intvalue); ASSERT_EQUALS(1, valueOfTok("3 - (a ? b : 2);", "-").intvalue); ASSERT_EQUALS(6, valueOfTok("3 * (a ? b : 2);", "*").intvalue); ASSERT_EQUALS(6, valueOfTok("13 / (a ? b : 2);", "/").intvalue); ASSERT_EQUALS(1, valueOfTok("13 % (a ? b : 2);", "%").intvalue); ASSERT_EQUALS(0, valueOfTok("3 == (a ? b : 2);", "==").intvalue); ASSERT_EQUALS(1, valueOfTok("3 != (a ? b : 2);", "!=").intvalue); ASSERT_EQUALS(1, valueOfTok("3 > (a ? b : 2);", ">").intvalue); ASSERT_EQUALS(1, valueOfTok("3 >= (a ? b : 2);", ">=").intvalue); ASSERT_EQUALS(0, valueOfTok("3 < (a ? b : 2);", "<").intvalue); ASSERT_EQUALS(0, valueOfTok("3 <= (a ? b : 2);", "<=").intvalue); ASSERT_EQUALS(1, valueOfTok("(UNKNOWN_TYPE)1;","(").intvalue); ASSERT(tokenValues("(UNKNOWN_TYPE)1000;","(").empty()); // don't know if there is truncation, sign extension ASSERT_EQUALS(255, valueOfTok("(unsigned char)~0;", "(").intvalue); ASSERT_EQUALS(0, valueOfTok("(int)0;", "(").intvalue); ASSERT_EQUALS(3, valueOfTok("(int)(1+2);", "(").intvalue); ASSERT_EQUALS(0, valueOfTok("(UNKNOWN_TYPE*)0;","(").intvalue); ASSERT_EQUALS(100, valueOfTok("(int)100.0;", "(").intvalue); ASSERT_EQUALS(10, valueOfTok("x = static_cast(10);", "( 10 )").intvalue); // Don't calculate if there is UB ASSERT(tokenValues(";-1<<10;","<<").empty()); ASSERT(tokenValues(";10<<-1;","<<").empty()); ASSERT(tokenValues(";10<<64;","<<").empty()); ASSERT(tokenValues(";-1>>10;",">>").empty()); ASSERT(tokenValues(";10>>-1;",">>").empty()); ASSERT(tokenValues(";10>>64;",">>").empty()); // calculation using 1,2 variables/values code = "void f(int x) {\n" " a = x+456;\n" " if (x==123) {}" "}"; ASSERT_EQUALS(579, valueOfTok(code, "+").intvalue); code = "void f(int x, int y) {\n" " a = x+y;\n" " if (x==123 || y==456) {}" "}"; ASSERT_EQUALS(0, valueOfTok(code, "+").intvalue); code = "void f(int x) {\n" " a = x+x;\n" " if (x==123) {}" "}"; ASSERT_EQUALS(246, valueOfTok(code, "+").intvalue); code = "void f(int x, int y) {\n" " a = x*x;\n" " if (x==2) {}\n" " if (x==4) {}\n" "}"; std::list values = tokenValues(code,"*"); ASSERT_EQUALS(2U, values.size()); ASSERT_EQUALS(4, values.front().intvalue); ASSERT_EQUALS(16, values.back().intvalue); code = "void f(int x) {\n" " if (x == 3) {}\n" " a = x * (1 - x - 1);\n" "}"; ASSERT_EQUALS(-9, valueOfTok(code, "*").intvalue); // addition of different variables with known values code = "int f(int x) {\n" " int a = 1;\n" " while (x!=3) { x+=a; }\n" " return x/a;\n" "}\n"; ASSERT_EQUALS(3, valueOfTok(code, "/").intvalue); // ? : code = "x = y ? 2 : 3;\n"; values = tokenValues(code,"?"); ASSERT_EQUALS(2U, values.size()); ASSERT_EQUALS(2, values.front().intvalue); ASSERT_EQUALS(3, values.back().intvalue); code = "void f(int a) { x = a ? 2 : 3; }\n"; values = tokenValues(code,"?"); ASSERT_EQUALS(2U, values.size()); ASSERT_EQUALS(2, values.front().intvalue); ASSERT_EQUALS(3, values.back().intvalue); code = "x = (2<5) ? 2 : 3;\n"; values = tokenValues(code, "?"); ASSERT_EQUALS(1U, values.size()); ASSERT_EQUALS(2, values.front().intvalue); code = "x = 123 ? : 456;\n"; values = tokenValues(code, "?"); ASSERT_EQUALS(1U, values.size()); ASSERT_EQUALS(123, values.empty() ? 0 : values.front().intvalue); code = "int f() {\n" " const int i = 1;\n" " int x = i < 0 ? 0 : 1;\n" " return x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 1)); // ~ code = "x = ~0U;"; settings.platform(cppcheck::Platform::Native); // ensure platform is native values = tokenValues(code,"~"); ASSERT_EQUALS(1U, values.size()); ASSERT_EQUALS(~0U, values.back().intvalue); // ! code = "void f(int x) {\n" " a = !x;\n" " if (x==0) {}\n" "}"; values = tokenValues(code,"!"); ASSERT_EQUALS(1U, values.size()); ASSERT_EQUALS(1, values.back().intvalue); // unary minus code = "void f(int x) {\n" " a = -x;\n" " if (x==10) {}\n" "}"; values = tokenValues(code,"-"); ASSERT_EQUALS(1U, values.size()); ASSERT_EQUALS(-10, values.back().intvalue); // Logical and code = "void f(bool b) {\n" " bool x = false && b;\n" " bool a = x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 0)); code = "void f(bool b) {\n" " bool x = b && false;\n" " bool a = x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 0)); code = "void f(bool b) {\n" " bool x = true && b;\n" " bool a = x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 1)); code = "void f(bool b) {\n" " bool x = b && true;\n" " bool a = x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 1)); // Logical or code = "void f(bool b) {\n" " bool x = true || b;\n" " bool a = x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 1)); code = "void f(bool b) {\n" " bool x = b || true;\n" " bool a = x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 1)); code = "void f(bool b) {\n" " bool x = false || b;\n" " bool a = x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 0)); code = "void f(bool b) {\n" " bool x = b || false;\n" " bool a = x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 0)); code = "bool f() {\n" " bool a = (4 == 3);\n" " bool b = (3 == 3);\n" " return a || b;\n" "}\n"; values = tokenValues(code, "%oror%"); ASSERT_EQUALS(1, values.size()); if (!values.empty()) { ASSERT_EQUALS(true, values.front().isIntValue()); ASSERT_EQUALS(true, values.front().isKnown()); ASSERT_EQUALS(1, values.front().intvalue); } // function call => calculation 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); } // Comparison of string values = tokenValues("f(\"xyz\" == \"xyz\");", "=="); // implementation defined ASSERT_EQUALS(0U, values.size()); // <- no value values = tokenValues("f(\"xyz\" == 0);", "=="); ASSERT_EQUALS(1U, values.size()); ASSERT_EQUALS(0, values.front().intvalue); values = tokenValues("f(0 == \"xyz\");", "=="); ASSERT_EQUALS(1U, values.size()); ASSERT_EQUALS(0, values.front().intvalue); values = tokenValues("f(\"xyz\" != 0);", "!="); ASSERT_EQUALS(1U, values.size()); ASSERT_EQUALS(1, values.front().intvalue); values = tokenValues("f(0 != \"xyz\");", "!="); ASSERT_EQUALS(1U, values.size()); ASSERT_EQUALS(1, values.front().intvalue); } void valueFlowSizeof() { const char *code; std::list values; // array size code = "void f() {\n" " char a[10];" " x = sizeof(*a);\n" "}"; values = tokenValues(code,"( *"); ASSERT_EQUALS(1U, values.size()); ASSERT_EQUALS(1, values.back().intvalue); code = "enum testEnum : uint32_t { a };\n" "sizeof(testEnum);"; values = tokenValues(code,"( testEnum"); ASSERT_EQUALS(1U, values.size()); ASSERT_EQUALS(4, values.back().intvalue); #define CHECK3(A, B, C) \ do { \ code = "void f() {\n" \ " x = sizeof(" A ");\n" \ "}"; \ values = tokenValues(code,"( " C " )"); \ ASSERT_EQUALS(1U, values.size()); \ ASSERT_EQUALS(B, values.back().intvalue);\ } while(false) #define CHECK(A, B) CHECK3(A, B, A) // standard types CHECK("void *", settings.sizeof_pointer); CHECK("char", 1U); CHECK("short", settings.sizeof_short); CHECK("int", settings.sizeof_int); CHECK("long", settings.sizeof_long); CHECK3("long long", settings.sizeof_long_long, "long"); CHECK("wchar_t", settings.sizeof_wchar_t); CHECK("float", settings.sizeof_float); CHECK("double", settings.sizeof_double); CHECK3("long double", settings.sizeof_long_double, "double"); // string/char literals CHECK("\"asdf\"", 5); CHECK("L\"asdf\"", 5 * settings.sizeof_wchar_t); CHECK("u8\"asdf\"", 5); // char8_t CHECK("u\"asdf\"", 5 * 2); // char16_t CHECK("U\"asdf\"", 5 * 4); // char32_t CHECK("'a'", 1U); CHECK("'ab'", settings.sizeof_int); CHECK("L'a'", settings.sizeof_wchar_t); CHECK("u8'a'", 1U); // char8_t CHECK("u'a'", 2U); // char16_t CHECK("U'a'", 4U); // char32_t #undef CHECK #undef CHECK3 // array size code = "void f() {\n" " struct S *a[10];" " x = sizeof(a) / sizeof(a[0]);\n" "}"; values = tokenValues(code,"/"); ASSERT_EQUALS(1U, values.size()); ASSERT_EQUALS(10, values.back().intvalue); #define CHECK(A, B, C, D) \ do { \ code = "enum " A " E " B " { E0, E1 };\n" \ "void f() {\n" \ " x = sizeof(" C ");\n" \ "}"; \ values = tokenValues(code,"( " C " )"); \ ASSERT_EQUALS(1U, values.size()); \ ASSERT_EQUALS(D, values.back().intvalue); \ } while(false) // enums CHECK("", "", "E", settings.sizeof_int); // typed enums CHECK("", ": char", "E", 1U); CHECK("", ": signed char", "E", 1U); CHECK("", ": unsigned char", "E", 1U); CHECK("", ": short", "E", settings.sizeof_short); CHECK("", ": signed short", "E", settings.sizeof_short); CHECK("", ": unsigned short", "E", settings.sizeof_short); CHECK("", ": int", "E", settings.sizeof_int); CHECK("", ": signed int", "E", settings.sizeof_int); CHECK("", ": unsigned int", "E", settings.sizeof_int); CHECK("", ": long", "E", settings.sizeof_long); CHECK("", ": signed long", "E", settings.sizeof_long); CHECK("", ": unsigned long", "E", settings.sizeof_long); CHECK("", ": long long", "E", settings.sizeof_long_long); CHECK("", ": signed long long", "E", settings.sizeof_long_long); CHECK("", ": unsigned long long", "E", settings.sizeof_long_long); CHECK("", ": wchar_t", "E", settings.sizeof_wchar_t); CHECK("", ": size_t", "E", settings.sizeof_size_t); // enumerators CHECK("", "", "E0", settings.sizeof_int); // typed enumerators CHECK("", ": char", "E0", 1U); CHECK("", ": signed char", "E0", 1U); CHECK("", ": unsigned char", "E0", 1U); CHECK("", ": short", "E0", settings.sizeof_short); CHECK("", ": signed short", "E0", settings.sizeof_short); CHECK("", ": unsigned short", "E0", settings.sizeof_short); CHECK("", ": int", "E0", settings.sizeof_int); CHECK("", ": signed int", "E0", settings.sizeof_int); CHECK("", ": unsigned int", "E0", settings.sizeof_int); CHECK("", ": long", "E0", settings.sizeof_long); CHECK("", ": signed long", "E0", settings.sizeof_long); CHECK("", ": unsigned long", "E0", settings.sizeof_long); CHECK("", ": long long", "E0", settings.sizeof_long_long); CHECK("", ": signed long long", "E0", settings.sizeof_long_long); CHECK("", ": unsigned long long", "E0", settings.sizeof_long_long); CHECK("", ": wchar_t", "E0", settings.sizeof_wchar_t); CHECK("", ": size_t", "E0", settings.sizeof_size_t); // class typed enumerators CHECK("class", ": char", "E :: E0", 1U); CHECK("class", ": signed char", "E :: E0", 1U); CHECK("class", ": unsigned char", "E :: E0", 1U); CHECK("class", ": short", "E :: E0", settings.sizeof_short); CHECK("class", ": signed short", "E :: E0", settings.sizeof_short); CHECK("class", ": unsigned short", "E :: E0", settings.sizeof_short); CHECK("class", ": int", "E :: E0", settings.sizeof_int); CHECK("class", ": signed int", "E :: E0", settings.sizeof_int); CHECK("class", ": unsigned int", "E :: E0", settings.sizeof_int); CHECK("class", ": long", "E :: E0", settings.sizeof_long); CHECK("class", ": signed long", "E :: E0", settings.sizeof_long); CHECK("class", ": unsigned long", "E :: E0", settings.sizeof_long); CHECK("class", ": long long", "E :: E0", settings.sizeof_long_long); CHECK("class", ": signed long long", "E :: E0", settings.sizeof_long_long); CHECK("class", ": unsigned long long", "E :: E0", settings.sizeof_long_long); CHECK("class", ": wchar_t", "E :: E0", settings.sizeof_wchar_t); CHECK("class", ": size_t", "E :: E0", settings.sizeof_size_t); #undef CHECK #define CHECK(A, B) \ do { \ code = "enum E " A " { E0, E1 };\n" \ "void f() {\n" \ " E arrE[] = { E0, E1 };\n" \ " x = sizeof(arrE);\n" \ "}"; \ values = tokenValues(code,"( arrE )"); \ ASSERT_EQUALS(1U, values.size()); \ ASSERT_EQUALS(B * 2U, values.back().intvalue);\ } while(false) // enum array CHECK("", settings.sizeof_int); // typed enum array CHECK(": char", 1U); CHECK(": signed char", 1U); CHECK(": unsigned char", 1U); CHECK(": short", settings.sizeof_short); CHECK(": signed short", settings.sizeof_short); CHECK(": unsigned short", settings.sizeof_short); CHECK(": int", settings.sizeof_int); CHECK(": signed int", settings.sizeof_int); CHECK(": unsigned int", settings.sizeof_int); CHECK(": long", settings.sizeof_long); CHECK(": signed long", settings.sizeof_long); CHECK(": unsigned long", settings.sizeof_long); CHECK(": long long", settings.sizeof_long_long); CHECK(": signed long long", settings.sizeof_long_long); CHECK(": unsigned long long", settings.sizeof_long_long); CHECK(": wchar_t", settings.sizeof_wchar_t); CHECK(": size_t", settings.sizeof_size_t); #undef CHECK #define CHECK(A, B) \ do { \ code = "enum class E " A " { E0, E1 };\n" \ "void f() {\n" \ " E arrE[] = { E::E0, E::E1 };\n" \ " x = sizeof(arrE);\n" \ "}"; \ values = tokenValues(code,"( arrE )"); \ ASSERT_EQUALS(1U, values.size()); \ ASSERT_EQUALS(B * 2U, values.back().intvalue);\ } while(false) // enum array CHECK("", settings.sizeof_int); // typed enum array CHECK(": char", 1U); CHECK(": signed char", 1U); CHECK(": unsigned char", 1U); CHECK(": short", settings.sizeof_short); CHECK(": signed short", settings.sizeof_short); CHECK(": unsigned short", settings.sizeof_short); CHECK(": int", settings.sizeof_int); CHECK(": signed int", settings.sizeof_int); CHECK(": unsigned int", settings.sizeof_int); CHECK(": long", settings.sizeof_long); CHECK(": signed long", settings.sizeof_long); CHECK(": unsigned long", settings.sizeof_long); CHECK(": long long", settings.sizeof_long_long); CHECK(": signed long long", settings.sizeof_long_long); CHECK(": unsigned long long", settings.sizeof_long_long); CHECK(": wchar_t", settings.sizeof_wchar_t); CHECK(": size_t", settings.sizeof_size_t); #undef CHECK code = "uint16_t arr[10];\n" "x = sizeof(arr);"; values = tokenValues(code,"( arr )"); ASSERT_EQUALS(1U, values.size()); ASSERT_EQUALS(10 * sizeof(std::uint16_t), values.back().intvalue); } void valueFlowErrorPath() { const char *code; code = "void f() {\n" " int x = 53;\n" " a = x;\n" "}\n"; ASSERT_EQUALS("2,Assignment 'x=53', assigned value is 53\n", getErrorPathForX(code, 3U)); code = "void f(int y) {\n" " int x = y;\n" " a = x;\n" " y += 12;\n" " if (y == 32) {}" "}\n"; ASSERT_EQUALS("5,Assuming that condition 'y==32' is not redundant\n" "4,Compound assignment '+=', assigned value is 20\n" "2,Assignment 'x=y', assigned value is 20\n", getErrorPathForX(code, 3U)); code = "void f1(int x) {\n" " a = x;\n" "}\n" "void f2() {\n" " int x = 3;\n" " f1(x+1);\n" "}\n"; ASSERT_EQUALS("5,Assignment 'x=3', assigned value is 3\n" "6,Calling function 'f1', 1st argument 'x+1' value is 4\n", getErrorPathForX(code, 2U)); code = "void f(int a) {\n" " int x;\n" " for (x = a; x < 50; x++) {}\n" " b = x;\n" "}\n"; ASSERT_EQUALS("3,After for loop, x has value 50\n", getErrorPathForX(code, 4U)); } 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)); // 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)); } 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)); code = "void f(int x) {\n" " a = x;\n" " x -= 2;\n" " if (x == 4);\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 2U, 6)); code = "void f(int x) {\n" " a = x;\n" " x *= 2;\n" " if (x == 42);\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 2U, 21)); code = "void f(int x) {\n" " a = x;\n" " x /= 5;\n" " if (x == 42);\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 2U, 210)); // bailout: assignment bailout("void f(int x) {\n" " x = y;\n" " if (x == 123) {}\n" "}"); ASSERT_EQUALS_WITHOUT_LINENUMBERS( "[test.cpp:2]: (debug) valueflow.cpp::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" // <- 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)); code = "void f(int *x) {\n" " int a = (x && *x == '1');\n" " int b = a ? atoi(x) : 0;\n" // <- x is not 0 " if (x==0) {}\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 0)); } void valueFlowBeforeConditionFunctionCall() { // function calls const char *code; 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).c_str(), 2U, 1)); ASSERT_EQUALS(false, testValueOfX((std::string("void setx(int &x);")+code).c_str(), 2U, 1)); ASSERT_EQUALS(true, 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)); } void valueFlowBeforeConditionLoop() { // while, for, do-while const char *code; 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 => don't 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" " a = x;\n" " for (; x!=1; x++) { }\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 2U, 1)); code = "void f(menu *x) {\n" " a = x->parent;\n" " for (i=0;(i<10) && (x!=0); i++) { x = x->next; }\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 2U, 0)); 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 => don't 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 => don't use condition " a = x;\n" " for (i=1;i<=count;i++) {\n" " BUGON(x==0)\n" " x = x.next;\n" " }\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 2U, 0)); } void valueFlowBeforeConditionTernaryOp() { // bailout: ?: const char *code; bailout("void f(int x) {\n" " y = ((x<0) ? x : ((x==2)?3:4));\n" "}"); ASSERT_EQUALS_WITHOUT_LINENUMBERS( "[test.cpp:2]: (debug) valueflow.cpp::valueFlowConditionExpressions bailout: Skipping function due to incomplete variable y\n", errout.str()); bailout("int f(int x) {\n" " int r = x ? 1 / x : 0;\n" " if (x == 0) {}\n" "}"); 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(const s *x) {\n" " x->a = 0;\n" " if (x ? x->a : 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_WITHOUT_LINENUMBERS( "[test.cpp:2]: (debug) valueflow.cpp::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" // <- condition must be false if x is 7 in next line " if (x == 7) { }\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 2U, 7)); code = "void f(int x, bool abc) {\n" " a = x;\n" " if (abc) { x = 7; }\n" // <- condition is probably true " if (x == 7) { }\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 2U, 7)); } void valueFlowBeforeConditionGlobalVariables() { const char *code; // handle global variables code = "int x;\n" "void f() {\n" " int a = x;\n" " if (x == 123) {}\n" "}"; ASSERT_EQUALS(true, testValueOfX(code,3,123)); // bailout when there is function call code = "class Fred { int x; void clear(); void f(); };\n" "void Fred::f() {\n" " int a = x;\n" " clear();\n" // <- x might be assigned " if (x == 234) {}\n" "}"; ASSERT_EQUALS(false, testValueOfX(code,3,234)); } void valueFlowBeforeConditionSwitch() { // 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_WITHOUT_LINENUMBERS( "[test.cpp:3]: (debug) valueflow.cpp::valueFlowConditionExpressions bailout: Skipping function due to incomplete variable a\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_WITHOUT_LINENUMBERS( "[test.cpp:3]: (debug) valueflow.cpp::valueFlowConditionExpressions bailout: Skipping function due to incomplete variable a\n", errout.str()); } void valueFlowBeforeConditionMacro() { // bailout: condition is a expanded macro bailout("#define M if (x==123) {}\n" "void f(int x) {\n" " a = x;\n" " M;\n" "}"); ASSERT_EQUALS_WITHOUT_LINENUMBERS( "[test.cpp:3]: (debug) valueflow.cpp::valueFlowConditionExpressions bailout: Skipping function due to incomplete variable a\n" "[test.cpp:4]: (debug) valueflow.cpp:1260:(valueFlow) bailout: variable 'x', condition is defined in macro\n", errout.str()); bailout("#define FREE(obj) ((obj) ? (free((char *) (obj)), (obj) = 0) : 0)\n" // #8349 "void f(int *x) {\n" " a = x;\n" " FREE(x);\n" "}"); ASSERT_EQUALS_WITHOUT_LINENUMBERS( "[test.cpp:3]: (debug) valueflow.cpp::valueFlowConditionExpressions bailout: Skipping function due to incomplete variable a\n" "[test.cpp:4]: (debug) valueflow.cpp:1260:(valueFlow) bailout: variable 'x', condition is defined in macro\n", errout.str()); } void valueFlowBeforeConditionGoto() { // 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_WITHOUT_LINENUMBERS( "[test.cpp:3]: (debug) valueflow.cpp::valueFlowConditionExpressions bailout: Skipping function due to incomplete variable a\n", errout.str()); // #5721 - FP bailout("static void f(int rc) {\n" " ABC* abc = getabc();\n" " if (!abc) { goto out };\n" "\n" " abc->majortype = 0;\n" " if (FAILED(rc)) {}\n" "\n" "out:\n" " if (abc) {}\n" "}\n"); } void valueFlowBeforeConditionForward() { const char* code; code = "void f(int a) {\n" " int x = a;\n" " if (a == 123) {}\n" " int b = x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 123)); code = "void f(int a) {\n" " int x = a;\n" " if (a != 123) {}\n" " int b = x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 123)); } void valueFlowAfterAssign() { const char *code; code = "void f() {\n" " int x = 123;\n" " a = x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 123)); code = "void f() {\n" " bool x = 32;\n" " a = x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 1)); code = "void f() {\n" " int x = 123;\n" " a = sizeof(x);\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 123)); code = "void f() {\n" " int x = 123;\n" " a = 2 + x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 123)); code = "void f() {\n" " const int x(321);\n" " a = x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 321)); code = "void f() {\n" " int x = 9;\n" " --x;\n" " return x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 9)); ASSERT_EQUALS(true, testValueOfX(code, 4U, 8)); ASSERT_EQUALS("2,Assignment 'x=9', assigned value is 9\n" "3,x is decremented', new value is 8\n", getErrorPathForX(code, 4U)); code = "void x() {\n" " int x = value ? 6 : 0;\n" " x =\n" " 1 + x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 7)); code = "void f() {\n" " int x = 0;\n" " y = x += z;\n" " return x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); code = "void f() {\n" " static int x = 2;\n" " x++;\n" " return x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 2)); code = "void f() {\n" " static int x = 2;\n" " a >> x;\n" " return x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 2)); code = "void f() {\n" " static int x = 0;\n" " if (x==0) x = getX();\n" " return x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); // truncation code = "int f() {\n" " int x = 1.5;\n" " return x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 1)); code = "int f() {\n" " unsigned char x = 0x123;\n" " return x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 0x23)); code = "int f() {\n" " signed char x = 0xfe;\n" " return x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, -2)); // function code = "void f() {\n" " char *x = 0;\n" " int success = getx((char**)&x);\n" " if (success) x[0] = 0;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); code = "void f() {\n" " char *x = 0;\n" " getx(reinterpret_cast(&x));\n" " *x = 0;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); // lambda code = "void f() {\n" " int x = 0;\n" " Q q = [&]() {\n" " if (x > 0) {}\n" " x++;\n" " };\n" " dosomething(q);\n" "}\n"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); code = "void f() {\n" " int x = 0;\n" " dostuff([&]() {\n" " if (x > 0) {}\n" " x++;\n" " });\n" " dosomething(q);\n" "}\n"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); code = "int f() {\n" " int x = 1;\n" " dostuff([&]() {\n" " x = y;\n" " });\n" " return x;\n" "}\n"; ASSERT_EQUALS(false, testValueOfX(code, 6U, 1)); // ?: code = "void f() {\n" " int x = 8;\n" " a = ((x > 10) ?\n" " x : 0);\n" // <- x is not 8 "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 8)); code = "void f() {\n" // #6973 " char *x = \"\";\n" " a = ((x[0] == 'U') ?\n" " x[1] : 0);\n" // <- x is not "" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, "\"\"", ValueFlow::Value::ValueType::TOK)); code = "void f() {\n" // #6973 " char *x = getenv (\"LC_ALL\");\n" " if (x == NULL)\n" " x = \"\";\n" "\n" " if ( (x[0] == 'U') &&\n" // x can be "" " (x[1] ?\n" // x can't be "" " x[3] :\n" // x can't be "" " x[2] ))\n" // x can't be "" " {}\n" "}\n"; ASSERT_EQUALS(true, testValueOfX(code, 6U, "\"\"", ValueFlow::Value::ValueType::TOK)); ASSERT_EQUALS(false, testValueOfX(code, 7U, "\"\"", ValueFlow::Value::ValueType::TOK)); ASSERT_EQUALS(false, testValueOfX(code, 8U, "\"\"", ValueFlow::Value::ValueType::TOK)); ASSERT_EQUALS(false, testValueOfX(code, 9U, "\"\"", ValueFlow::Value::ValueType::TOK)); code = "void f() {\n" // #7599 " t *x = 0;\n" " y = (a ? 1 : x\n" // <- x is 0 " && x->y ? 1 : 2);" // <- x is not 0 "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 0)); ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); code = "void f() {\n" // #7599 " t *x = 0;\n" " y = (a ? 1 : !x\n" // <- x is 0 " || x->y ? 1 : 2);" // <- x is not 0 "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 0)); ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); // if/else code = "void f() {\n" " int x = 123;\n" " if (condition) return;\n" " a = 2 + x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 123)); code = "void f() {\n" " int x = 1;\n" " if (condition) x = 2;\n" " a = 2 + x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 1)); ASSERT_EQUALS(true, testValueOfX(code, 4U, 2)); code = "void f() {\n" " int x = 123;\n" " if (condition1) x = 456;\n" " if (condition2) x = 789;\n" " a = 2 + x;\n" // <- either assignment "x=123" is redundant or x can be 123 here. "}"; TODO_ASSERT_EQUALS(true, false, testValueOfX(code, 5U, 123)); code = "void f(int a) {\n" " int x = 123;\n" " if (a > 1)\n" " ++x;\n" " else\n" " ++x;\n" " return 2 + x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 123)); code = "void f() {\n" " int x = 1;\n" " if (condition1) x = 2;\n" " else return;\n" " a = 2 + x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 5U, 1)); code = "void f(){\n" " int x = 0;\n" " if (a>=0) { x = getx(); }\n" " if (x==0) { return; }\n" " return 123 / x;\n" "}\n"; ASSERT_EQUALS(false, testValueOfX(code, 5U, 0)); code = "void f() {\n" " X *x = getx();\n" " if(0) { x = 0; }\n" " else { x->y = 1; }\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); code = "void f() {\n" // #6239 " int x = 4;\n" " if(1) { x = 0; }\n" " a = x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 4)); code = "void f() {\n" " int x = 32;\n" " if (x>=32) return;\n" " a[x]=0;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 32)); code = "void f() {\n" " int x = 32;\n" " if (x>=32) {\n" " a[x] = 0;\n" // <- should have possible value 32 " return;\n" " }\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 32)); code = "void f() {\n" " int x = 33;\n" " if (x==33) goto fail;\n" " a[x]=0;\n" "fail:\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 33)); code = "void f() {\n" " int x = 32;\n" " if (a==1) { z=x+12; }\n" " if (a==2) { z=x+32; }\n" " z = x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 32)); ASSERT_EQUALS(true, testValueOfX(code, 4U, 32)); ASSERT_EQUALS(true, testValueOfX(code, 5U, 32)); code = "void f() {\n" // #5656 - FP " int x = 0;\n" " if (!x) {\n" " x = getx();\n" " }\n" " y = x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 6U, 0)); code = "void f(int y) {\n" // alias " int x = y;\n" " if (y == 54) {}\n" " else { a = x; }\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 54)); code = "void f () {\n" " ST * x = g_pST;\n" " if (x->y == 0) {\n" " x = NULL;\n" " return 1;\n" " }\n" " a = x->y;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 7U, 0)); code = "void f () {\n" " ST * x = g_pST;\n" " if (x->y == 0) {\n" " x = NULL;\n" " goto label;\n" " }\n" " a = x->y;\n" "label:\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 7U, 0)); code = "void f() {\n" // #5752 - FP " int *x = 0;\n" " if (x && *x == 123) {\n" " getx(*x);\n" " }\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); code = "void f() {\n" " int x = 0;\n" " if (!x) {}\n" " else { y = x; }\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); code = "void f() {\n" // #6118 - FP " int x = 0;\n" " x = x & 0x1;\n" " if (x == 0) { x = 2; }\n" " y = 42 / x;\n" // <- x is 2 "}"; ASSERT_EQUALS(false, testValueOfX(code, 5U, 0)); ASSERT_EQUALS(true, testValueOfX(code, 5U, 2)); code = "void f() {\n" // #6118 - FN " int x = 0;\n" " x = x & 0x1;\n" " if (x == 0) { x += 2; }\n" " y = 42 / x;\n" // <- x is 2 "}"; ASSERT_EQUALS(false, testValueOfX(code, 5U, 0)); ASSERT_EQUALS(true, testValueOfX(code, 5U, 2)); code = "void f(int mode) {\n" " struct ABC *x;\n" "\n" " if (mode) { x = &y; }\n" " else { x = NULL; }\n" "\n" " if (!x) exit(1);\n" "\n" " a = x->a;\n" // <- x can't be 0 "}"; ASSERT_EQUALS(false, testValueOfX(code, 9U, 0)); code = "void f(int i) {\n" " bool x = false;\n" " if (i == 0) { x = true; }\n" " else if (x && i == 1) {}\n" "}\n"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 0)); code = "void f(int i) {\n" " bool x = false;\n" " while(i > 0) {\n" " i++;\n" " if (i == 0) { x = true; }\n" " else if (x && i == 1) {}\n" " }\n" "}\n"; ASSERT_EQUALS(true, testValueOfX(code, 6U, 0)); // multivariables code = "void f(int a) {\n" " int x = a;\n" " if (a!=132) { b = x; }\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 132)); code = "void f(int a) {\n" " int x = a;\n" " b = x;\n" // <- line 3 " if (a!=132) {}\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 132)); code = "void f() {\n" " int a;\n" " if (n) { a = n; }\n" " else { a = 0; }\n" " int x = a;\n" " if (a > 0) { a = b / x; }\n" // <- line 6 "}"; ASSERT_EQUALS(false, testValueOfX(code, 6U, 0)); // x is not 0 at line 6 code = "void f(int x1) {\n" // #6086 " int x = x1;\n" " if (x1 >= 3) {\n" " return;\n" " }\n" " a = x;\n" // <- x is not 3 "}"; ASSERT_EQUALS(false, testValueOfX(code, 6U, 3)); code = "int f(int *x) {\n" // #5980 " if (!x) {\n" " switch (i) {\n" " default:\n" " throw std::runtime_error(msg);\n" " };\n" " }\n" " return *x;\n" // <- x is not 0 "}"; ASSERT_EQUALS(false, testValueOfX(code, 8U, 0)); code = "void f(int a) {\n" // #6826 " int x = a ? a : 87;\n" " if (a && x) {}\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 87)); code = "void f() {\n" " int first=-1, x=0;\n" " do {\n" " if (first >= 0) { a = x; }\n" // <- x is not 0 " first++; x=3;\n" " } while (1);\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); // pointer/reference to x code = "int f(void) {\n" " int x = 2;\n" " int *px = &x;\n" " for (int i = 0; i < 1; i++) {\n" " *px = 1;\n" " }\n" " return x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 7U, 2)); code = "int f(void) {\n" " int x = 5;\n" " int &rx = x;\n" " for (int i = 0; i < 1; i++) {\n" " rx = 1;\n" " }\n" " return x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 7U, 5)); // break code = "void f() {\n" " for (;;) {\n" " int x = 1;\n" " if (!abc()) {\n" " x = 2;\n" " break;\n" " }\n" " a = x;\n" // <- line 8 " }\n" "}\n"; ASSERT_EQUALS(false, testValueOfX(code, 8U, 2)); // x is not 2 at line 8 code = "void f() {\n" " int x;\n" " switch (ab) {\n" " case A: x = 12; break;\n" " case B: x = 34; break;\n" " }\n" " v = x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 7U, 12)); ASSERT_EQUALS(true, testValueOfX(code, 7U, 34)); code = "void f() {\n" // #5981 " int x;\n" " switch (ab) {\n" " case A: x = 12; break;\n" " case B: x = 34; break;\n" " }\n" " switch (ab) {\n" " case A: v = x; break;\n" // <- x is not 34 " }\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 8U, 34)); // while/for code = "void f() {\n" // #6138 " ENTRY *x = 0;\n" " while (x = get()) {\n" " set(x->value);\n" // <- x is not 0 " }\n" "}\n"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); code = "void f(const int *buf) {\n" " int x = 0;\n" " for (int i = 0; i < 10; i++) {\n" " if (buf[i] == 123) {\n" " x = i;\n" " break;\n" " }\n" " }\n" " a = x;\n" // <- x can be 0 "}\n"; ASSERT_EQUALS(true, testValueOfX(code, 9U, 0)); // x can be 0 at line 9 code = "void f(const int *buf) {\n" " int x = 111;\n" " bool found = false;\n" " for (int i = 0; i < 10; i++) {\n" " if (buf[i] == 123) {\n" " x = i;\n" " found = true;\n" " break;\n" " }\n" " }\n" " if (found)\n" " a = x;\n" // <- x can't be 111 "}\n"; ASSERT_EQUALS(false, testValueOfX(code, 12U, 111)); // x can not be 111 at line 9 code = "void f(const int *buf) {\n" " int x = 0;\n" " for (int i = 0; i < 10; i++) {\n" " if (buf[i] == 123) {\n" " x = i;\n" " ;\n" // <- no break " }\n" " }\n" " a = x;\n" // <- x can't be 0 "}\n"; ASSERT_EQUALS(true, testValueOfX(code, 9U, 0)); // x can be 0 at line 9 ASSERT_EQUALS(false, testValueOfXKnown(code, 9U, 0)); // x can't be known at line 9 code = "void f(const int *buf) {\n" " int x = 0;\n" " for (int i = 0; i < 10; i++) {\n" " if (buf[i] == 123) {\n" " x = i;\n" " ;\n" // <- no break " } else {\n" " x = 1;\n" " }\n" " }\n" " a = x;\n" // <- x can't be 0 "}\n"; ASSERT_EQUALS(false, testValueOfX(code, 11U, 0)); // x can't be 0 at line 11 code = "void f(const int *buf) {\n" " int x = 0;\n" " while (++i < 10) {\n" " if (buf[i] == 123) {\n" " x = i;\n" " break;\n" " }\n" " }\n" " a = x;\n" // <- x can be 0 "}\n"; ASSERT_EQUALS(true, testValueOfX(code, 9U, 0)); // x can be 0 at line 9 code = "void f(const int a[]) {\n" // #6616 " const int *x = 0;\n" " for (int i = 0; i < 10; i = *x) {\n" // <- x is not 0 " x = a[i];\n" " }\n" "}\n"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 0)); // alias code = "void f() {\n" // #7778 " int x = 0;\n" " int *p = &x;\n" " x = 3;\n" " *p = 2;\n" " a = x;\n" "}\n"; ASSERT_EQUALS(false, testValueOfX(code, 6U, 3)); TODO_ASSERT_EQUALS(true, false, testValueOfX(code, 6U, 2)); code = "struct Fred {\n" " static void Create(std::unique_ptr wilma);\n" " Fred(std::unique_ptr wilma);\n" " std::unique_ptr mWilma;\n" "};\n" "void Fred::Create(std::unique_ptr wilma) {\n" " auto fred = std::make_shared(std::move(wilma));\n" " fred->mWilma.reset();\n" "}\n" "Fred::Fred(std::unique_ptr wilma)\n" " : mWilma(std::move(wilma)) {}\n"; ASSERT_EQUALS(0, tokenValues(code, "mWilma (").size()); code = "void g(unknown*);\n" "int f() {\n" " int a = 1;\n" " unknown c[] = {{&a}};\n" " g(c);\n" " int x = a;\n" " return x;\n" "}\n"; ASSERT_EQUALS(false, testValueOfXKnown(code, 7U, 1)); ASSERT_EQUALS(true, testValueOfXInconclusive(code, 7U, 1)); code = "void g(unknown&);\n" "int f() {\n" " int a = 1;\n" " unknown c{&a};\n" " g(c);\n" " int x = a;\n" " return x;\n" "}\n"; ASSERT_EQUALS(false, testValueOfXKnown(code, 7U, 1)); ASSERT_EQUALS(true, testValueOfXInconclusive(code, 7U, 1)); } void valueFlowAfterCondition() { const char *code; // in if code = "void f(int x) {\n" " if (x == 123) {\n" " a = x;\n" " }\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 123)); code = "void f(int x) {\n" " if (x != 123) {\n" " a = x;\n" " }\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 123)); code = "void f(int x) {\n" " if (x > 123) {\n" " a = x;\n" " }\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 124)); ASSERT_EQUALS(false, testValueOfX(code, 3U, 123)); code = "void f(int x) {\n" " if (x < 123) {\n" " a = x;\n" " }\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 122)); ASSERT_EQUALS(false, testValueOfX(code, 3U, 123)); // ---- code = "void f(int x) {\n" " if (123 < x) {\n" " a = x;\n" " }\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 124)); ASSERT_EQUALS(false, testValueOfX(code, 3U, 123)); code = "void f(int x) {\n" " if (123 > x) {\n" " a = x;\n" " }\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 122)); ASSERT_EQUALS(false, testValueOfX(code, 3U, 123)); // in else code = "void f(int x) {\n" " if (x == 123) {}\n" " else a = x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 123)); code = "void f(int x) {\n" " if (x != 123) {}\n" " else a = x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 123)); // after if code = "void f(int x) {\n" " if (x == 10) {\n" " x++;\n" " }\n" " a = x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 5U, 10)); TODO_ASSERT_EQUALS(true, false, testValueOfX(code, 5U, 11)); // ! code = "void f(int x) {\n" " if (!x) { a = x; }\n" " else a = x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 2U, 0)); code = "void f(int x, int y) {\n" " if (!(x&&y)) { return; }\n" " a = x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 0)); code = "void f(int x) {\n" " if (!x) { { throw new string(); }; }\n" " a = x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 0)); code = "void f(int x) {\n" " if (x != 123) { throw ""; }\n" " a = x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 123)); code = "void f(int x) {\n" " if (x != 123) { }\n" " else { throw ""; }\n" " a = x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 123)); code = "void f(int x) {\n" " if (x == 123) { }\n" " else { throw ""; }\n" " a = x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 123)); code = "void f(int x) {\n" " if (x < 123) { }\n" " else { a = x; }\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 123)); code = "void f(int x) {\n" " if (x < 123) { throw \"\"; }\n" " a = x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 123)); code = "void f(int x) {\n" " if (x < 123) { }\n" " else { throw \"\"; }\n" " a = x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 122)); ASSERT_EQUALS(false, testValueOfX(code, 4U, 123)); code = "void f(int x) {\n" " if (x > 123) { }\n" " else { a = x; }\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 123)); code = "void f(int x) {\n" " if (x > 123) { throw \"\"; }\n" " a = x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 123)); code = "void f(int x) {\n" " if (x > 123) { }\n" " else { throw \"\"; }\n" " a = x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 124)); ASSERT_EQUALS(false, testValueOfX(code, 4U, 123)); code = "void f(int x) {\n" " if (x < 123) { return; }\n" " else { return; }\n" " a = x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 124)); ASSERT_EQUALS(false, testValueOfX(code, 4U, 123)); // if (var) code = "void f(int x) {\n" " if (x) { a = x; }\n" // <- x is not 0 " else { b = x; }\n" // <- x is 0 " c = x;\n" // <- x might be 0 "}"; ASSERT_EQUALS(false, testValueOfX(code, 2U, 0)); ASSERT_EQUALS(true, testValueOfX(code, 3U, 0)); ASSERT_EQUALS(true, testValueOfX(code, 4U, 0)); // After while code = "void f(int x) {\n" " while (x != 3) {}\n" " a = x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 3)); code = "void f(int x) {\n" " while (11 != (x = dostuff())) {}\n" " a = x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 11)); code = "void f(int x) {\n" " while (11 != (x = dostuff()) && y) {}\n" " a = x;\n" "}"; TODO_ASSERT_EQUALS(true, false, testValueOfX(code, 3U, 11)); code = "void f(int x) {\n" " while (x = dostuff()) {}\n" " a = x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 0)); code = "void f(const Token *x) {\n" // #5866 " x = x->next();\n" " while (x) { x = x->next(); }\n" " if (x->str()) {}\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 0)); code = "void f(const Token *x) {\n" " while (0 != (x = x->next)) {}\n" " x->ab = 0;\n" "}\n"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 0)); code = "void f(const Token* x) {\n" " while (0 != (x = x->next)) {}\n" " if (x->str) {\n" // <- possible value 0 " x = y;\n" // <- this caused some problem " }\n" "}\n"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 0)); // conditional code after if/else/while code = "void f(int x) {\n" " if (x == 2) {}\n" " if (x > 0)\n" " a = x;\n" // <- TODO, x can be 2 " else\n" " b = x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 2)); ASSERT_EQUALS(false, testValueOfX(code, 6U, 2)); // condition with 2nd variable code = "void f(int x) {\n" " int y = 0;\n" " if (x == 7) { y = 1; }\n" " if (!y)\n" " a = x;\n" // <- x can not be 7 here "}"; ASSERT_EQUALS(false, testValueOfX(code, 5U, 7)); code = "void f(struct X *x) {\n" " bool b = TRUE;\n" " if(x) { }\n" " else\n" " b = FALSE;\n" " if (b)\n" " abc(x->value);\n" // <- x is not 0 "}\n"; ASSERT_EQUALS(false, testValueOfX(code, 7U, 0)); // In condition, after && and || code = "void f(int x) {\n" " a = (x != 3 ||\n" " x);\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 3)); code = "void f(int x) {\n" " a = (x == 4 &&\n" " x);\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 4)); // protected usage with && code = "void f(const Token* x) {\n" " if (x) {}\n" " for (; x && \n" " x->str() != y; x = x->next()) {}\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 0)); ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); code = "void f(const Token* x) {\n" " if (x) {}\n" " if (x && \n" " x->str() != y) {}\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 0)); ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); // return code = "void f(int x) {\n" // #6024 " if (x == 5) {\n" " if (z) return; else return;\n" " }\n" " a = x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 5U, 5)); code = "void f(int x) {\n" // #6730 " if (x == 5) {\n" " if (z) continue; else throw e;\n" " }\n" " a = x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 5U, 5)); // TODO: float code = "void f(float x) {\n" " if (x == 0.5) {}\n" " a = x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 0)); // aliased variable code = "void f() {\n" " int x = 1;\n" " int *data = &x;\n" " if (!x) {\n" " calc(data);\n" " a = x;\n" // <- x might be changed by calc " }\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 6U, 0)); code = "int* g();\n" "int f() {\n" " int * x;\n" " x = g();\n" " if (x) { printf(\"\"); }\n" " return *x;\n" "}\n"; ASSERT_EQUALS(false, testValueOfXKnown(code, 6U, 0)); ASSERT_EQUALS(true, testValueOfX(code, 6U, 0)); // volatile variable code = "void foo(const volatile int &x) {\n" " if (x==1) {\n" " return x;\n" " }" "}"; ASSERT_EQUALS(false, testValueOfXKnown(code, 3U, 1)); code = "void foo(const std::atomic &x) {\n" " if (x==2) {\n" " return x;\n" " }" "}"; ASSERT_EQUALS(false, testValueOfXKnown(code, 3U, 2)); code = "int f(int i, int j) {\n" " if (i == 0) {\n" " if (j < 0)\n" " return 0;\n" " i = j+1;\n" " }\n" " int x = i;\n" " return x;\n" "}\n"; ASSERT_EQUALS(false, testValueOfX(code, 8U, 0)); code = "int f(int i, int j) {\n" " if (i == 0) {\n" " if (j < 0)\n" " return 0;\n" " if (j < 0)\n" " i = j+1;\n" " }\n" " int x = i;\n" " return x;\n" "}\n"; ASSERT_EQUALS(true, testValueOfX(code, 9U, 0)); code = "void g(long& a);\n" "void f(long a) {\n" " if (a == 0)\n" " return;\n" " if (a > 1)\n" " g(a);\n" " int x = a;\n" " return x;\n" "}\n"; ASSERT_EQUALS(false, testValueOfXImpossible(code, 8U, 0)); code = "int foo(int n) {\n" " if( n>= 8 ) {\n" " while(true) {\n" " n -= 8;\n" " if( n < 8 )\n" " break;\n" " }\n" " int x = n == 0;\n" " return x;\n" " }\n" "}\n"; ASSERT_EQUALS(false, testValueOfXKnown(code, 9U, 0)); code = "bool c();\n" "long f() {\n" " bool stop = false;\n" " while (!stop) {\n" " if (c())\n" " stop = true;\n" " break;\n" " }\n" " int x = !stop;\n" " return x;\n" "}\n"; ASSERT_EQUALS(false, testValueOfXImpossible(code, 10U, 1)); ASSERT_EQUALS(false, testValueOfXKnown(code, 10U, 0)); code = "int f(int a, int b) {\n" " if (!a && !b)\n" " return;\n" " if ((!a && b) || (a && !b))\n" " return;\n" " int x = a;\n" // <- a is _not_ 0 " return x;\n" "}\n"; ASSERT_EQUALS(false, testValueOfX(code, 7U, 0)); code = "void f(int x, int y) {\n" " if (x && y)\n" " return;\n" " int a = x;\n" "}\n"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 0)); ASSERT_EQUALS(false, testValueOfXKnown(code, 4U, 0)); ASSERT_EQUALS(false, testValueOfXImpossible(code, 4U, 1)); code = "int f(std::vector a, std::vector b) {\n" " if (a.empty() && b.empty())\n" " return 0;\n" " bool x = a.empty() && !b.empty();\n" " return x;\n" "}\n"; ASSERT_EQUALS(false, testValueOfXKnown(code, 5U, 0)); ASSERT_EQUALS(false, testValueOfXKnown(code, 5U, 1)); ASSERT_EQUALS(false, testValueOfXImpossible(code, 5U, 0)); ASSERT_EQUALS(false, testValueOfXImpossible(code, 5U, 1)); } void valueFlowAfterConditionExpr() { const char* code; code = "void f(int* p) {\n" " if (p[0] == 123) {\n" " int x = p[0];\n" " int a = x;\n" " }\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 123)); code = "void f(int y) {\n" " if (y+1 == 123) {\n" " int x = y+1;\n" " int a = x;\n" " }\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 123)); code = "void f(int y) {\n" " if (y+1 == 123) {\n" " int x = y+2;\n" " int a = x;\n" " }\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 124)); code = "void f(int y, int z) {\n" " if (y+z == 123) {\n" " int x = y+z;\n" " int a = x;\n" " }\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 123)); code = "void f(int y, int z) {\n" " if (y+z == 123) {\n" " y++;\n" " int x = y+z;\n" " int a = x;\n" " }\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 5U, 123)); code = "void f(int y) {\n" " if (y++ == 123) {\n" " int x = y++;\n" " int a = x;\n" " }\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 123)); ASSERT_EQUALS(false, testValueOfX(code, 4U, 124)); ASSERT_EQUALS(false, testValueOfX(code, 4U, 125)); code = "struct A {\n" " bool g() const;\n" "};\n" "void f(A a) {\n" " if (a.g()) {\n" " bool x = a.g();\n" " bool a = x;\n" " }\n" "}\n"; ASSERT_EQUALS(false, testValueOfXKnown(code, 7U, 0)); code = "struct A {\n" " bool g() const;\n" "};\n" "void f(A a) {\n" " if (a.g()) {\n" " bool x = !a.g();\n" " bool a = x;\n" " }\n" "}\n"; ASSERT_EQUALS(true, testValueOfXKnown(code, 7U, 0)); code = "struct A {\n" " bool g() const;\n" "};\n" "void f(A a) {\n" " if (!a.g()) {\n" " bool x = a.g();\n" " bool a = x;\n" " }\n" "}\n"; ASSERT_EQUALS(true, testValueOfXKnown(code, 7U, 0)); code = "void f(std::vector v) {\n" " if (v.size() == 3) {\n" " if (v.size() == 1) {\n" " int x = 1;\n" " int a = x;\n" " }\n" " }\n" "}\n"; ASSERT_EQUALS(true, testValueOfXKnown(code, 5U, 1)); } void valueFlowAfterConditionSeveralNot() { const char *code; code = "int f(int x, int y) {\n" " if (x!=0) {}\n" " return y/x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 0)); code = "int f(int x, int y) {\n" " if (!!(x != 0)) {\n" " return y/x;\n" "}\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 0)); code = "int f(int x, int y) {\n" " if (!!!(x != 0)) {\n" " return y/x;\n" "}\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 0)); code = "int f(int x, int y) {\n" " if (!!!!(x != 0)) {\n" " return y/x;\n" "}\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 0)); } void valueFlowForwardCompoundAssign() { const char *code; code = "void f() {\n" " int x = 123;\n" " x += 43;\n" " return x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 166)); ASSERT_EQUALS("2,Assignment 'x=123', assigned value is 123\n" "3,Compound assignment '+=', assigned value is 166\n", getErrorPathForX(code, 4U)); code = "void f() {\n" " int x = 123;\n" " x /= 0;\n" // don't crash when evaluating x/=0 " return x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 123)); code = "void f() {\n" " float x = 123.45;\n" " x += 67;\n" " return x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 123.45F + 67, 0.01F)); code = "void f() {\n" " int x = 123;\n" " x >>= 1;\n" " return x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 61)); code = "void f() {\n" " int x = 123;\n" " x <<= 1;\n" " return x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 246)); } void valueFlowForwardCorrelatedVariables() { const char *code; code = "void f(int x = 0) {\n" " bool zero(x==0);\n" " if (zero) a = x;\n" // <- x is 0 " else b = x;\n" // <- x is not 0 "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 0)); ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); } void valueFlowForwardModifiedVariables() { const char *code; code = "void f(bool b) {\n" " int x = 0;\n" " if (b) x = 1;\n" " else b = x;\n" "}"; ASSERT_EQUALS(true, testValueOfXKnown(code, 4U, 0)); code = "void f(int i) {\n" " int x = 0;\n" " if (i == 0) \n" " x = 1;\n" " else if (!x && i == 1) \n" " int b = x;\n" "}\n"; ASSERT_EQUALS(true, testValueOfXKnown(code, 5U, 0)); ASSERT_EQUALS(true, testValueOfXKnown(code, 6U, 0)); } void valueFlowForwardFunction() { const char *code; code = "class C {\n" "public:\n" " C(int &i);\n" // non-const argument => might be changed "};\n" "int f() {\n" " int x=1;\n" " C c(x);\n" " return x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 8U, 1)); code = "class C {\n" "public:\n" " C(const int &i);\n" // const argument => is not changed "};\n" "int f() {\n" " int x=1;\n" " C c(x);\n" " return x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 8U, 1)); code = "int f(int *);\n" "int g() {\n" " const int a = 1;\n" " int x = 11;\n" " c = (a && f(&x));\n" " if (x == 42) {}\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 6U, 11)); } void valueFlowForwardTernary() { const char *code; code = "int f() {\n" " int x=5;\n" " a = b ? init1(&x) : init2(&x);\n" " return 1 + x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 5)); ASSERT_EQUALS(false, testValueOfX(code, 4U, 5)); code = "int f(int *p) {\n" // #9008 - gcc ternary ?: " if (p) return;\n" " x = *p ? : 1;\n" // <- no explicit expr0 "}"; testValueOfX(code, 1U, 0); // do not crash code = "void f(int a) {\n" // #8784 " int x = 13;\n" " if (a == 1) x = 26;\n" " return a == 1 ? x : 0;\n" // <- x is 26 "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 13)); ASSERT_EQUALS(true, testValueOfX(code, 4U, 26)); } void valueFlowForwardLambda() { const char *code; code = "void f() {\n" " int x=1;\n" " auto f = [&](){ a=x; }\n" // x is not 1 " x = 2;\n" " f();\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 1)); TODO_ASSERT_EQUALS(true, false, testValueOfX(code, 3U, 2)); code = "void f() {\n" " int x=3;\n" " auto f = [&](){ a=x; }\n" // todo: x is 3 " f();\n" "}"; TODO_ASSERT_EQUALS(true, false, testValueOfX(code, 3U, 3)); } void valueFlowForwardTryCatch() { const char *code; code = "void g1();\n" "void g2();\n" "void f()\n {" " bool x = false;\n" " try {\n" " g1();\n" " x = true;\n" " g2();\n" " }\n" " catch (...) {\n" " if (x) {}\n" " }\n" "}\n"; ASSERT_EQUALS(true, testValueOfX(code, 11U, 1)); ASSERT_EQUALS(false, testValueOfXKnown(code, 11U, 1)); code = "void g1();\n" "void g2();\n" "void f()\n {" " bool x = true;\n" " try {\n" " g1();\n" " g2();\n" " }\n" " catch (...) {\n" " if (x) {}\n" " }\n" "}\n"; ASSERT_EQUALS(true, testValueOfXKnown(code, 10U, 1)); } void valueFlowBitAnd() { const char *code; code = "int f(int a) {\n" " int x = a & 0x80;\n" " return x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code,3U,0)); ASSERT_EQUALS(true, testValueOfX(code,3U,0x80)); code = "int f(int a) {\n" " int x = a & 0x80 ? 1 : 2;\n" " return x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code,3U,0)); ASSERT_EQUALS(false, testValueOfX(code,3U,0x80)); code = "int f() {\n" " int x = (19 - 3) & 15;\n" " return x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code,3U,0)); ASSERT_EQUALS(false, testValueOfX(code,3U,16)); } void valueFlowForwardInconclusiveImpossible() { const char *code; code = "void foo() {\n" " bool valid = f1();\n" " if (!valid) return;\n" " std::tie(endVal, valid) = f2();\n" " bool x = !valid;" " bool b = x;" // <- not always true "}\n"; ASSERT_EQUALS(false, testValueOfXKnown(code, 6U, 1)); } void valueFlowRightShift() { const char *code; /* Set some temporary fixed values to simplify testing */ const Settings settingsTmp = settings; settings.int_bit = 32; settings.long_bit = 64; settings.long_long_bit = MathLib::bigint_bits * 2; code = "int f(int a) {\n" " int x = (a & 0xff) >> 16;\n" " return x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code,3U,0)); code = "int f(unsigned int a) {\n" " int x = (a % 123) >> 16;\n" " return x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code,3U,0)); code = "int f(int y) {\n" " int x = (y & 0xFFFFFFF) >> 31;\n" " return x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3u, 0)); code = "int f(int y) {\n" " int x = (y & 0xFFFFFFF) >> 32;\n" " return x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 3u, 0)); code = "int f(short y) {\n" " int x = (y & 0xFFFFFF) >> 31;\n" " return x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3u, 0)); code = "int f(short y) {\n" " int x = (y & 0xFFFFFF) >> 32;\n" " return x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 3u, 0)); code = "int f(long y) {\n" " int x = (y & 0xFFFFFF) >> 63;\n" " return x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3u, 0)); code = "int f(long y) {\n" " int x = (y & 0xFFFFFF) >> 64;\n" " return x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 3u, 0)); code = "int f(long long y) {\n" " int x = (y & 0xFFFFFF) >> 63;\n" " return x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3u, 0)); code = "int f(long long y) {\n" " int x = (y & 0xFFFFFF) >> 64;\n" " return x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 3u, 0)); code = "int f(long long y) {\n" " int x = (y & 0xFFFFFF) >> 121;\n" " return x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 3u, 0)); code = "int f(long long y) {\n" " int x = (y & 0xFFFFFF) >> 128;\n" " return x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 3u, 0)); settings = settingsTmp; } void valueFlowFwdAnalysis() { const char *code; std::list values; code = "void f() {\n" " struct Foo foo;\n" " foo.x = 1;\n" " x = 0 + foo.x;\n" // <- foo.x is 1 "}"; values = tokenValues(code, "+"); ASSERT_EQUALS(1U, values.size()); ASSERT_EQUALS(true, values.front().isKnown()); ASSERT_EQUALS(true, values.front().isIntValue()); ASSERT_EQUALS(1, values.front().intvalue); code = "void f() {\n" " S s;\n" " s.x = 1;\n" " int y = 10;\n" " while (s.x < y)\n" // s.x does not have known value " s.x++;\n" "}"; values = tokenValues(code, "<"); ASSERT_EQUALS(1, values.size()); ASSERT(values.front().isPossible()); ASSERT_EQUALS(true, values.front().intvalue); code = "void f() {\n" " S s;\n" " s.x = 37;\n" " int y = 10;\n" " while (s.x < y)\n" // s.x has a known value " y--;\n" "}"; values = tokenValues(code, ". x <"); ASSERT(values.size() == 1 && values.front().isKnown() && values.front().isIntValue() && values.front().intvalue == 37); code = "void f() {\n" " Hints hints;\n" " hints.x = 1;\n" " if (foo)\n" " hints.x = 2;\n" " x = 0 + foo.x;\n" // <- foo.x is possible 1, possible 2 "}"; values = tokenValues(code, "+"); TODO_ASSERT_EQUALS(2U, 0U, values.size()); // should be 2 // FP: Condition '*b>0' is always true code = "bool dostuff(const char *x, const char *y);\n" "void fun(char *s, int *b) {\n" " for (int i = 0; i < 42; ++i) {\n" " if (dostuff(s, \"1\")) {\n" " *b = 1;\n" " break;\n" " }\n" " }\n" " if (*b > 0) {\n" // *b does not have known value " }\n" "}"; values = tokenValues(code, ">"); ASSERT_EQUALS(1, values.size()); ASSERT(values.front().isPossible()); ASSERT_EQUALS(true, values.front().intvalue); code = "void foo() {\n" " struct ISO_PVD_s pvd;\n" " pvd.descr_type = 0xff;\n" " do {\n" " if (pvd.descr_type == 0xff) {}\n" " dostuff(&pvd);\n" " } while (condition)\n" "}"; values = tokenValues(code, "=="); ASSERT_EQUALS(true, values.empty()); // for loops code = "struct S { int x; };\n" // #9036 "void foo(struct S s) {\n" " for (s.x = 0; s.x < 127; s.x++) {}\n" "}"; values = tokenValues(code, "<"); // TODO: comparison can be true or false ASSERT_EQUALS(true, values.empty()); } void valueFlowSwitchVariable() { const char *code; code = "void f(int x) {\n" " a = x - 1;\n" // <- x can be 14 " switch (x) {\n" " case 14: a=x+2; break;\n" // <- x is 14 " };\n" " a = x;\n" // <- x can be 14 "}"; ASSERT_EQUALS(true, testConditionalValueOfX(code, 2U, 14)); TODO_ASSERT_EQUALS(true, false, testConditionalValueOfX(code, 4U, 14)); TODO_ASSERT_EQUALS(true, false, testConditionalValueOfX(code, 6U, 14)); ValueFlow::Value value1 = valueOfTok(code, "-"); ASSERT_EQUALS(13, value1.intvalue); ASSERT(!value1.isKnown()); ValueFlow::Value value2 = valueOfTok(code, "+"); TODO_ASSERT_EQUALS(16, 0, value2.intvalue); TODO_ASSERT_EQUALS(true, false, value2.isKnown()); } void valueFlowForLoop() { const char *code; ValueFlow::Value value; 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" " int x;\n" " for (x = 2; x < 1; x++)\n" " a[x] = 0;\n" // <- not 2 " b = x;\n" // 2 "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 2)); ASSERT_EQUALS(true, testValueOfX(code, 5U, 2)); code = "void f() {\n" " int x;\n" " for (x = 2; x < 1; ++x)\n" " a[x] = 0;\n" // <- not 2 " b = x;\n" // 2 "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 2)); ASSERT_EQUALS(true, testValueOfX(code, 5U, 2)); code = "enum AB {A,B};\n" // enum => handled by valueForLoop2 "void f() {\n" " int x;\n" " for (x = 1; x < B; ++x)\n" " a[x] = 0;\n" // <- not 1 "}"; ASSERT_EQUALS(false, testValueOfX(code, 5U, 1)); code = "void f(int a) {\n" " for (int x = a; x < 10; x++)\n" " a[x] = 0;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 9)); code = "void f() {\n" " for (int x = 0; x < 5; x += 2)\n" " a[x] = 0;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 0)); ASSERT_EQUALS(true, testValueOfX(code, 3U, 4)); code = "void f() {\n" " for (int x = 0; x < 10; x = x + 2)\n" " a[x] = 0;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 0)); ASSERT_EQUALS(true, testValueOfX(code, 3U, 8)); ASSERT_EQUALS(false, testValueOfX(code, 3U, 10)); code = "void f() {\n" " for (int x = 0; x < 10; x = x / 0)\n" " a[x] = 0;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 0)); // don't crash code = "void f() {\n" " for (int x = 0; x < 10; x++)\n" " x<4 ?\n" " a[x] : 0;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 0)); ASSERT_EQUALS(true, testValueOfX(code, 3U, 9)); ASSERT_EQUALS(false, testValueOfX(code, 4U, 9)); code = "void f() {\n" " for (int x = 0; x < 10; x++)\n" " x==0 ?\n" " 0 : a[x];\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); code = "void f() {\n" // #5223 " for (int x = 0; x < 300 && x < 18; x++)\n" " x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 0)); ASSERT_EQUALS(true, testValueOfX(code, 3U, 17)); ASSERT_EQUALS(false, testValueOfX(code, 3U, 299)); code = "void f() {\n" " int x;\n" " for (int i = 0; x = bar[i]; i++)\n" " x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); code = "void f() {\n" " const char abc[] = \"abc\";\n" " int x;\n" " for (x = 0; abc[x] != '\\0'; x++) {}\n" " a[x] = 0;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 5U, 3)); code = "void f() {\n" // #5939 " int x;\n" " for (int x = 0; (x = do_something()) != 0;)\n" " x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); code = "void f() {\n" " int x;\n" " for (int x = 0; x < 10 && y = do_something();)\n" " x;\n" "}"; TODO_ASSERT_EQUALS(true, false, testValueOfX(code, 4U, 0)); code = "void f() {\n" " int x,y;\n" " for (x = 0, y = 0; x < 10, y < 10; x++, y++)\n" // usage of , " x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 0)); code = "void foo(double recoveredX) {\n" " for (double x = 1e-18; x < 1e40; x *= 1.9) {\n" " double relativeError = (x - recoveredX) / x;\n" " }\n" "}\n"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 0)); // Ticket #7139 // "<<" in third expression of for code = "void f(void) {\n" " int bit, x;\n" " for (bit = 1, x = 0; bit < 128; bit = bit << 1, x++) {\n" " z = x;\n" // <- known value [0..6] " }\n" "}\n"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 0)); ASSERT_EQUALS(true, testValueOfX(code, 4U, 6)); ASSERT_EQUALS(false, testValueOfX(code, 4U, 7)); // && code = "void foo() {\n" " for (int x = 0; x < 10; x++) {\n" " if (x > 1\n" " && x) {}" // <- x is not 0 " }\n" "}\n"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); ASSERT_EQUALS(true, testValueOfX(code, 4U, 9)); code = "void foo() {\n" " for (int x = 0; x < 10; x++) {\n" " if (x < value\n" " && x) {}" // <- maybe x is not 9 " }\n" "}\n"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 9)); // || code = "void foo() {\n" " for (int x = 0; x < 10; x++) {\n" " if (x == 0\n" " || x) {}" // <- x is not 0 " }\n" "}\n"; ASSERT_EQUALS(false, testValueOfX(code, 4U, 0)); ASSERT_EQUALS(true, testValueOfX(code, 4U, 9)); // After loop code = "void foo() {\n" " int x;\n" " for (x = 0; x < 10; x++) {}\n" " a = x;\n" "}\n"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 10)); code = "void foo() {\n" " int x;\n" " for (x = 0; 2 * x < 20; x++) {}\n" " a = x;\n" "}\n"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 10)); code = "void foo() {\n" // related with #887 " int x;\n" " for (x = 0; x < 20; x++) {}\n" " a = x++;\n" "}\n"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 20)); code = "void f() {\n" " int x;\n" " for (x = 0; x < 5; x++) {}\n" " if (x == 5) {\n" " abort();\n" " }\n" " a = x;\n" // <- x can't be 5 "}"; ASSERT_EQUALS(false, testValueOfX(code, 7U, 5)); code = "void f() {\n" " int x;\n" " for (x = 0; x < 5; x++) {}\n" " if (x < 5) {}\n" " else return;\n" " a = x;\n" // <- x can't be 5 "}"; ASSERT_EQUALS(false, testValueOfX(code, 6U, 5)); // assert after for loop.. code = "static void f() {\n" " int x;\n" " int ctls[10];\n" " for (x = 0; x <= 10; x++) {\n" " if (cond)\n" " break;\n" " }\n" " assert(x <= 10);\n" " ctls[x] = 123;\n" // <- x can't be 11 "}\n"; ASSERT_EQUALS(false, testValueOfX(code, 9U, 11)); // hang code = "void f() {\n" " for(int i = 0; i < 20; i++)\n" " n = (int)(i < 10 || abs(negWander) < abs(negTravel));\n" "}"; testValueOfX(code,0,0); // <- don't hang // conditional code in loop code = "void f(int mask) {\n" // #6000 " for (int x = 10; x < 14; x++) {\n" " int bit = mask & (1 << i);\n" " if (bit) {\n" " if (bit == (1 << 10)) {}\n" " else { a = x; }\n" // <- x is not 10 " }\n" " }\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 6U, 10)); // #7886 - valueFlowForLoop must be called after valueFlowAfterAssign code = "void f() {\n" " int sz = 4;\n" " int x,y;\n" " for(x=0,y=0; x < sz && y < 10; x++)\n" " a = x;\n" // <- max value is 3 "}"; ASSERT_EQUALS(true, testValueOfX(code, 5U, 3)); code = "void f() {\n" " int x;\n" " for (x = 0; x < 10; x++)\n" " x;\n" "}"; std::list values = tokenValues(code, "x <"); ASSERT(std::none_of(values.begin(), values.end(), std::mem_fn(&ValueFlow::Value::isUninitValue))); // #9637 code = "void f() {\n" " unsigned int x = 0;\n" " for (x = 0; x < 2; x++) {}\n" "}\n"; value = valueOfTok(code, "x <"); ASSERT(value.isPossible()); ASSERT_EQUALS(value.intvalue, 0); code = "void f() {\n" " unsigned int x = 0;\n" " for (;x < 2; x++) {}\n" "}\n"; value = valueOfTok(code, "x <"); ASSERT(value.isPossible()); ASSERT_EQUALS(0, value.intvalue); code = "void f() {\n" " unsigned int x = 1;\n" " for (x = 0; x < 2; x++) {}\n" "}\n"; value = valueOfTok(code, "x <"); ASSERT(value.isPossible()); ASSERT_EQUALS(0, value.intvalue); } void valueFlowSubFunction() { const char *code; code = "int f(int size) {\n" " int x = 0;\n" " if(size>16) {\n" " x = size;\n" " int a = x;\n" " }\n" " return x;\n" "}\n" "void g(){\n" " f(42);\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 5U, 17)); ASSERT_EQUALS(true, testValueOfX(code, 5U, 42)); ASSERT_EQUALS(true, testValueOfX(code, 7U, 0)); ASSERT_EQUALS(true, testValueOfX(code, 7U, 17)); ASSERT_EQUALS(true, testValueOfX(code, 7U, 42)); code = "void g(int, int) {}\n" "void f(int x, int y) {\n" " g(x, y);\n" "}\n" "void h() {\n" " f(0, 0);\n" " f(1, 1);\n" " f(2, 2);\n" " f(3, 3);\n" " f(4, 4);\n" " f(5, 5);\n" " f(6, 6);\n" " f(7, 7);\n" " f(8, 8);\n" " f(9, 9);\n" "}\n"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 0)); ASSERT_EQUALS(true, testValueOfX(code, 3U, 1)); ASSERT_EQUALS(true, testValueOfX(code, 3U, 2)); ASSERT_EQUALS(true, testValueOfX(code, 3U, 3)); ASSERT_EQUALS(true, testValueOfX(code, 3U, 4)); ASSERT_EQUALS(true, testValueOfX(code, 3U, 5)); ASSERT_EQUALS(true, testValueOfX(code, 3U, 6)); ASSERT_EQUALS(true, testValueOfX(code, 3U, 7)); ASSERT_EQUALS(true, testValueOfX(code, 3U, 8)); ASSERT_EQUALS(true, testValueOfX(code, 3U, 9)); code = "int f(int i, int j) {\n" " if (i == j) {\n" " int x = i;\n" " return x;\n" " }\n" " return 0;\n" "}\n" "int g(int x) {\n" " f(x, -1);\n" "}\n"; ASSERT_EQUALS(true, testValueOfX(code, 4U, -1)); } void valueFlowFunctionReturn() { const char *code; code = "int f1(int x) {\n" " return x+1;\n" "}\n" "void f2() {\n" " x = 10 - f1(2);\n" "}"; ASSERT_EQUALS(7, valueOfTok(code, "-").intvalue); ASSERT_EQUALS(true, valueOfTok(code, "-").isKnown()); code = "int add(int x, int y) {\n" " return x+y;\n" "}\n" "void f2() {\n" " x = 1 * add(10+1,4);\n" "}"; ASSERT_EQUALS(15, valueOfTok(code, "*").intvalue); ASSERT_EQUALS(true, valueOfTok(code, "*").isKnown()); code = "int one() { return 1; }\n" "void f() { x = 1 * one(); }"; ASSERT_EQUALS(1, valueOfTok(code, "*").intvalue); ASSERT_EQUALS(true, valueOfTok(code, "*").isKnown()); code = "int add(int x, int y) {\n" " return x+y;\n" "}\n" "void f2() {\n" " x = 1 * add(1,add(2,3));\n" "}"; ASSERT_EQUALS(6, valueOfTok(code, "*").intvalue); ASSERT_EQUALS(true, valueOfTok(code, "*").isKnown()); code = "int f(int i, X x) {\n" " if (i)\n" " return g(std::move(x));\n" " g(x);\n" " return 0;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 4U, ValueFlow::Value::MoveKind::MovedVariable)); code = "class A\n" "{\n" " int f1(int x) {\n" " return x+1;\n" " }\n" " void f2() {\n" " x = 10 - f1(2);\n" " }\n" "};"; ASSERT_EQUALS(7, valueOfTok(code, "-").intvalue); ASSERT_EQUALS(true, valueOfTok(code, "-").isKnown()); code = "class A\n" "{\n" " virtual int f1(int x) {\n" " return x+1;\n" " }\n" " void f2() {\n" " x = 10 - f1(2);\n" " }\n" "};"; ASSERT_EQUALS(7, valueOfTok(code, "-").intvalue); ASSERT_EQUALS(false, valueOfTok(code, "-").isKnown()); } void valueFlowFunctionDefaultParameter() { const char *code; code = "class continuous_src_time {\n" " continuous_src_time(std::complex f, double st = 0.0, double et = infinity) {}\n" "};"; testValueOfX(code, 2U, 2); // Don't crash (#6494) } bool isNotKnownValues(const char code[], const char str[]) { for (const ValueFlow::Value &v : tokenValues(code, str)) { if (v.isKnown()) return false; } return true; } void knownValue() { const char *code; ValueFlow::Value value; ASSERT(valueOfTok("x = 1;", "1").isKnown()); // after assignment code = "void f() {\n" " int x = 1;\n" " return x + 2;\n" // <- known value "}"; value = valueOfTok(code, "+"); ASSERT_EQUALS(3, value.intvalue); ASSERT(value.isKnown()); { code = "void f() {\n" " int x = 15;\n" " if (x == 15) { x += 7; }\n" // <- condition is true "}"; value = valueOfTok(code, "=="); ASSERT_EQUALS(1, value.intvalue); ASSERT(value.isKnown()); code = "int f() {\n" " int a = 0, x = 0;\n" " a = index();\n" " if (a != 0)\n" " x = next();\n" " return x + 1;\n" "}\n"; value = valueOfTok(code, "+"); ASSERT(value.isPossible()); } code = "void f() {\n" " int x;\n" " if (ab) { x = 7; }\n" " return x + 2;\n" // <- possible value "}"; value = valueOfTok(code, "+"); ASSERT_EQUALS(9, value.intvalue); ASSERT(value.isPossible()); code = "void f(int c) {\n" " int x = 0;\n" " if (c) {} else { x++; }\n" " return x + 2;\n" // <- possible value "}"; ASSERT(isNotKnownValues(code, "+")); code = "void f() {\n" " int x = 0;\n" " dostuff(&x);\n" " if (x < 0) {}\n" "}\n"; ASSERT(isNotKnownValues(code, "<")); code = "void f() {\n" " int x = 0;\n" " dostuff(0 ? ptr : &x);\n" " if (x < 0) {}\n" "}\n"; ASSERT(isNotKnownValues(code, "<")); code = "void f() {\n" " int x = 0;\n" " dostuff(unknown ? ptr : &x);\n" " if (x < 0) {}\n" "}\n"; ASSERT(isNotKnownValues(code, "<")); code = "void f() {\n" " int x = 0;\n" " fred.dostuff(x);\n" " if (x < 0) {}\n" "}\n"; ASSERT(isNotKnownValues(code, "<")); code = "void dostuff(int x);\n" "void f() {\n" " int x = 0;\n" " dostuff(x);\n" " if (x < 0) {}\n" "}\n"; value = valueOfTok(code, "<"); ASSERT_EQUALS(0, value.intvalue); ASSERT(value.isKnown()); code = "void dostuff(int & x);\n" "void f() {\n" " int x = 0;\n" " dostuff(x);\n" " if (x < 0) {}\n" "}\n"; ASSERT(isNotKnownValues(code, "<")); code = "void dostuff(const int & x);\n" "void f() {\n" " int x = 0;\n" " dostuff(x);\n" " if (x < 0) {}\n" "}\n"; value = valueOfTok(code, "<"); ASSERT_EQUALS(0, value.intvalue); ASSERT(value.isKnown()); code = "void f() {\n" " int x = 0;\n" " do {\n" " if (x < 0) {}\n" " fred.dostuff(x);\n" " } while (abc);\n" "}\n"; ASSERT(isNotKnownValues(code, "<")); code = "int x;\n" "void f() {\n" " x = 4;\n" " while (1) {\n" " a = x+2;\n" " dostuff();\n" " }\n" "}"; ASSERT(isNotKnownValues(code, "+")); code = "void f() {\n" " int x = 0;\n" " if (y) { dostuff(x); }\n" " if (!x) {}\n" "}\n"; ASSERT(isNotKnownValues(code, "!")); code = "void f() {\n" " int x = 0;\n" " MACRO( v, { if (y) { x++; } } );\n" " if (!x) {}\n" "}\n"; ASSERT(isNotKnownValues(code, "!")); code = "void f() {\n" " int x = 0;\n" " for (int i = 0; i < 10; i++) {\n" " if (cond) {\n" " x = 1;\n" " break;\n" " }\n" " }\n" " if (!x) {}\n" // <- possible value "}"; ASSERT(isNotKnownValues(code, "!")); code = "void f() {\n" // #8356 " bool b = false;\n" " for(int x = 3; !b && x < 10; x++) {\n" // <- b has known value " for(int y = 4; !b && y < 20; y++) {}\n" " }\n" "}"; value = valueOfTok(code, "!"); ASSERT_EQUALS(1, value.intvalue); ASSERT(value.isKnown()); code = "void f() {\n" " int x = 0;\n" " switch (state) {\n" " case 1:\n" " x = 1;\n" " break;\n" " }\n" " if (!x) {}\n" // <- possible value "}"; ASSERT(isNotKnownValues(code, "!")); code = "void f() {\n" // #7049 " int x = 0;\n" " switch (a) {\n" " case 1:\n" " x = 1;\n" " case 2:\n" " if (!x) {}\n" // <- possible value " }\n" "}"; ASSERT(isNotKnownValues(code, "!")); code = "void f() {\n" " int x = 0;\n" " while (!x) {\n" // <- possible value " scanf(\"%d\", &x);\n" " }\n" "}"; value = valueOfTok(code, "!"); ASSERT_EQUALS(1, value.intvalue); ASSERT(value.isPossible()); code = "void f() {\n" " int x = 0;\n" " do { } while (++x < 12);\n" // <- possible value "}"; ASSERT(isNotKnownValues(code, "<")); code = "void f() {\n" " static int x = 0;\n" " return x + 1;\n" // <- known value "}\n"; value = valueOfTok(code, "+"); ASSERT_EQUALS(1, value.intvalue); ASSERT(value.isKnown()); code = "void f() {\n" " int x = 0;\n" "a:\n" " a = x + 1;\n" // <- possible value "}"; value = valueOfTok(code, "+"); ASSERT_EQUALS(1, value.intvalue); ASSERT(value.isPossible()); // in conditional code code = "void f(int x) {\n" " if (!x) {\n" " a = x+1;\n" // <- known value " }\n" "}"; value = valueOfTok(code, "+"); ASSERT_EQUALS(1, value.intvalue); ASSERT(value.isKnown()); code = "void f(int x) {\n" " if (a && 4==x && y) {\n" " a = x+12;\n" // <- known value " }\n" "}"; value = valueOfTok(code, "+"); ASSERT_EQUALS(16, value.intvalue); ASSERT(value.isKnown()); // after condition code = "int f(int x) {\n" " if (x == 4) {}\n" " return x + 1;\n" // <- possible value "}"; value = valueOfTok(code, "+"); ASSERT_EQUALS(5, value.intvalue); ASSERT(value.isPossible()); code = "int f(int x) {\n" " if (x < 2) {}\n" " else if (x >= 2) {}\n" // <- known value "}"; value = valueOfTok(code, ">="); ASSERT_EQUALS(1, value.intvalue); ASSERT(value.isKnown()); code = "int f(int x) {\n" " if (x < 2) {}\n" " else if (x > 2) {}\n" // <- possible value "}"; ASSERT(isNotKnownValues(code, ">")); // known and possible value code = "void f() {\n" " int x = 1;\n" " int y = 2 + x;\n" // <- known value, don't care about condition " if (x == 2) {}\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 1)); // value of x can be 1 ASSERT_EQUALS(false, testValueOfX(code, 3U, 2)); // value of x can't be 2 // calculation with known result code = "int f(int x) { a = x & 0; }"; // <- & is 0 value = valueOfTok(code, "&"); ASSERT_EQUALS(0, value.intvalue); ASSERT(value.isKnown()); // template parameters are not known code = "template void f() { a = X; }\n" "f<1>();"; value = valueOfTok(code, "1"); ASSERT_EQUALS(1, value.intvalue); ASSERT_EQUALS(false, value.isKnown()); } void valueFlowSizeofForwardDeclaredEnum() { const char *code = "enum E; sz=sizeof(E);"; valueOfTok(code, "="); // Don't crash (#7775) } void valueFlowGlobalVar() { const char *code; code = "int x;\n" "void f() {\n" " x = 4;\n" " a = x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 4)); code = "int x;\n" "void f() {\n" " if (x == 4) {}\n" " a = x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 4)); code = "int x;\n" "void f() {\n" " x = 42;\n" " unknownFunction();\n" " a = x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 5U, 42)); } void valueFlowGlobalConstVar() { const char* code; code = "const int x = 321;\n" "void f() {\n" " a = x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 321)); code = "void f(const int x = 1) {\n" " int a = x;\n" "}\n"; ASSERT_EQUALS(false, testValueOfXKnown(code, 2U, 1)); code = "volatile const int x = 42;\n" "void f(){ int a = x; }\n"; ASSERT_EQUALS(false, testValueOfXKnown(code, 2U, 42)); code = "static const int x = 42;\n" "void f(){ int a = x; }\n"; ASSERT_EQUALS(true, testValueOfX(code, 2U, 42)); } void valueFlowGlobalStaticVar() { const char *code; code = "static int x = 321;\n" "void f() {\n" " a = x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 321)); code = "static int x = 321;\n" "void f() {\n" " a = x;\n" "}" "void other() { x=a; }\n"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 321)); code = "static int x = 321;\n" "void f() {\n" " a = x;\n" "}" "void other() { p = &x; }\n"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 321)); code = "static int x = 321;\n" "void f() {\n" " a = x;\n" "}" "void other() { x++; }\n"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 321)); code = "static int x = 321;\n" "void f() {\n" " a = x;\n" "}" "void other() { foo(x); }\n"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 321)); code = "static int x = 1;\n" // compound assignment "void f() {\n" " a = x;\n" "}" "void other() { x += b; }\n"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 1)); } void valueFlowInlineAssembly() { const char* code = "void f() {\n" " int x = 42;\n" " asm(\"\");\n" " a = x;\n" "}"; ASSERT_EQUALS(false, testValueOfX(code, 5U, 42)); } void valueFlowSameExpression() { const char* code; code = "void f(int a) {\n" " bool x = a == a;\n" " bool b = x;\n" "}\n"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 1)); code = "void f(int a) {\n" " bool x = a != a;\n" " bool b = x;\n" "}\n"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 0)); code = "void f(int a) {\n" " int x = a - a;\n" " int b = x;\n" "}\n"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 0)); code = "void f(float a) {\n" " bool x = a == a;\n" " bool b = x;\n" "}\n"; ASSERT_EQUALS(false, testValueOfX(code, 3U, 1)); } void valueFlowUninit() { const char* code; std::list values; code = "void f() {\n" " int x;\n" " switch (x) {}\n" "}"; values = tokenValues(code, "x )"); ASSERT_EQUALS(true, values.size()==1U && values.front().isUninitValue()); code = "void f() {\n" " const C *c;\n" " if (c->x() == 4) {}\n" "}"; values = tokenValues(code, "c ."); ASSERT_EQUALS(true, values.size()==1U && values.front().isUninitValue()); code = "void f() {\n" " C *c;\n" " if (c->x() == 4) {}\n" "}"; values = tokenValues(code, "c ."); ASSERT_EQUALS(true, values.size()==1U && values.front().isUninitValue()); code = "void f() {\n" " int **x;\n" " y += 10;\n" " x = dostuff(sizeof(*x)*y);\n" "}"; ASSERT_EQUALS(0U, tokenValues(code, "x )").size()); // #8036 code = "void foo() {\n" " int x;\n" " f(x=3), return x+3;\n" "}"; values = tokenValues(code, "x +"); ASSERT_EQUALS(true, values.empty()); // ASSERT_EQUALS(1U, values.size()); // ASSERT(values.front().isIntValue()); // ASSERT_EQUALS(3, values.front().intvalue); // #8195 code = "void foo(std::istream &is) {\n" " int x;\n" " if (is >> x) {\n" " a = x;\n" " }\n" "}"; values = tokenValues(code, "x ; }"); ASSERT_EQUALS(true, values.empty()); // return (#8173) code = "int repeat() {\n" " const char *n;\n" " return((n=42) && *n == 'A');\n" "}"; values = tokenValues(code, "n =="); ASSERT_EQUALS(true, values.empty()); // #8233 code = "void foo() {\n" " int x;\n" " int y = 1;\n" " if (y>1)\n" " x = 1;\n" " else\n" " x = 1;\n" " if (x>1) {}\n" "}"; ASSERT_EQUALS(true, testValueOfXKnown(code, 8U, 1)); // #8348 - noreturn else code = "int test_input_int(int a, int b) {\n" " int x;\n" " if (a == 1)\n" " x = b;\n" " else\n" " abort();\n" " a = x + 1;\n" "}\n"; values = tokenValues(code, "x +"); ASSERT_EQUALS(true, values.empty()); // #8494 - overloaded operator & code = "void f() {\n" " int x;\n" " a & x;\n" "}"; values = tokenValues(code, "x ; }"); ASSERT_EQUALS(true, values.empty()); code = "void b(bool d, bool e) {\n" " int c;\n" " if (d)\n" " c = 0;\n" " if (e)\n" " goto;\n" " c++;\n" "}\n"; values = tokenValues(code, "c ++ ; }"); ASSERT_EQUALS(true, values.empty()); code = "void b(bool d, bool e) {\n" " int c;\n" " if (d)\n" " c = 0;\n" " if (e)\n" " return;\n" " c++;\n" "}\n"; values = tokenValues(code, "c ++ ; }"); ASSERT_EQUALS(true, values.empty()); code = "void b(bool d, bool e) {\n" " int c;\n" " if (d)\n" " c = 0;\n" " if (e)\n" " exit();\n" " c++;\n" "}\n"; values = tokenValues(code, "c ++ ; }"); ASSERT_EQUALS(true, values.empty()); code = "void b(bool d, bool e) {\n" " int c;\n" " if (d)\n" " c = 0;\n" " else if (e)\n" " c = 0;\n" " c++;\n" "}\n"; values = tokenValues(code, "c ++ ; }"); TODO_ASSERT_EQUALS(true, false, values.size() == 2); // ASSERT_EQUALS(true, values.front().isUninitValue() || values.back().isUninitValue()); // ASSERT_EQUALS(true, values.front().isPossible() || values.back().isPossible()); // ASSERT_EQUALS(true, values.front().intvalue == 0 || values.back().intvalue == 0); code = "void b(bool d, bool e) {\n" " int c;\n" " if (d)\n" " c = 0;\n" " else if (!d)\n" " c = 0;\n" " c++;\n" "}\n"; values = tokenValues(code, "c ++ ; }"); ASSERT_EQUALS(true, values.size() == 1); // TODO: Value should be known ASSERT_EQUALS(true, values.back().isPossible()); ASSERT_EQUALS(true, values.back().intvalue == 0); code = "void f() {\n" // sqlite " int szHdr;\n" " idx = (A<0x80) ? (szHdr = 0) : dostuff(A, (int *)&(szHdr));\n" " d = szHdr;\n" // szHdr can be 0. "}"; values = tokenValues(code, "szHdr ; }"); TODO_ASSERT_EQUALS(1, 0, values.size()); if (values.size() == 1) { ASSERT_EQUALS(false, values.front().isUninitValue()); } code = "void f () {\n" " int szHdr;\n" " idx = ((aKey<0x80) ? ((szHdr)=aKey), 1 : sqlite3GetVarint32(&(szHdr)));\n" " d = szHdr;\n" "}"; values = tokenValues(code, "szHdr ; }"); ASSERT_EQUALS(0, values.size()); // #9933 code = "struct MyStruct { size_t value; }\n" "\n" "void foo() {\n" " MyStruct x;\n" " fread(((char *)&x) + 0, sizeof(x), f);\n" " if (x.value < 432) {}\n" "}"; values = tokenValues(code, "x . value"); ASSERT_EQUALS(0, values.size()); // #10166 code = "int f(bool b) {\n" " int x;\n" " do {\n" " if (b) {\n" " x = 0;\n" " break;\n" " }\n" " } while (true);\n" " return x;\n" "}\n"; values = tokenValues(code, "x ; }", ValueFlow::Value::ValueType::UNINIT); ASSERT_EQUALS(0, values.size()); code = "int f(bool b) {\n" " int x;\n" " while (true) {\n" " if (b) {\n" " x = 0;\n" " break;\n" " }\n" " }\n" " return x;\n" "}\n"; values = tokenValues(code, "x ; }", ValueFlow::Value::ValueType::UNINIT); ASSERT_EQUALS(0, values.size()); code = "int f(bool b) {\n" " int x;\n" " for(;;) {\n" " if (b) {\n" " x = 0;\n" " break;\n" " }\n" " }\n" " return x;\n" "}\n"; values = tokenValues(code, "x ; }", ValueFlow::Value::ValueType::UNINIT); ASSERT_EQUALS(0, values.size()); code = "int f(bool b) {\n" " int x;\n" " switch (b) {\n" " case 1: {\n" " ret = 0;\n" " break;\n" " }\n" " }\n" " return x;\n" "}\n"; values = tokenValues(code, "x ; }", ValueFlow::Value::ValueType::UNINIT); ASSERT_EQUALS(0, values.size()); } void valueFlowConditionExpressions() { const char* code; // opposite condition code = "void f(int i, int j) {\n" " if (i == j) return;\n" " if(i != j) {}\n" "}\n"; ASSERT_EQUALS(true, valueOfTok(code, "!=").intvalue == 1); code = "void f(int i, int j) {\n" " if (i == j) return;\n" " i++;\n" " if (i != j) {}\n" "}\n"; ASSERT_EQUALS(false, valueOfTok(code, "!=").intvalue == 1); code = "void f(int i, int j, bool a) {\n" " if (a) {\n" " if (i == j) return;\n" " }\n" " if (i != j) {}\n" "}\n"; ASSERT_EQUALS(true, valueOfTok(code, "!=").intvalue == 1); ASSERT_EQUALS(false, valueOfTok(code, "!=").isKnown()); code = "void f(int i, int j, bool a) {\n" " if (i != j) {}\n" " if (i == j) return; \n" "}\n"; ASSERT_EQUALS(false, valueOfTok(code, "!=").intvalue == 1); // same expression code = "void f(int i, int j) {\n" " if (i != j) return;\n" " bool x = (i != j);\n" " bool b = x;\n" "}\n"; ASSERT_EQUALS(true, testValueOfXKnown(code, 4U, 0)); code = "void f(int i, int j) {\n" " if (i != j) return;\n" " i++;\n" " bool x = (i != j);\n" " bool b = x;\n" "}\n"; ASSERT_EQUALS(false, testValueOfXKnown(code, 5U, 0)); code = "void f(int i, int j, bool a) {\n" " if (a) {\n" " if (i != j) return;\n" " }\n" " bool x = (i != j);\n" " bool b = x;\n" "}\n"; ASSERT_EQUALS(false, testValueOfXKnown(code, 6U, 0)); code = "void f(int i, int j, bool a) {\n" " bool x = (i != j);\n" " bool b = x;\n" " if (i != j) return; \n" "}\n"; ASSERT_EQUALS(false, testValueOfXKnown(code, 3U, 0)); code = "void f(int i, int j, bool b) {\n" " if (i == j) { if(b) return; }\n" " if(i != j) {}\n" "}\n"; ASSERT_EQUALS(false, valueOfTok(code, "!=").intvalue == 1); code = "void f(bool b, int i, int j) {\n" " if (b || i == j) return;\n" " if(i != j) {}\n" "}\n"; ASSERT_EQUALS(true, valueOfTok(code, "!=").intvalue == 1); code = "void f(bool b, int i, int j) {\n" " if (b && i == j) return;\n" " if(i != j) {}\n" "}\n"; ASSERT_EQUALS(true, tokenValues(code, "!=").empty()); code = "void f(int i, int j) {\n" " if (i == j) {\n" " if (i != j) {}\n" " }\n" "}\n"; ASSERT_EQUALS(true, valueOfTok(code, "!=").intvalue == 0); code = "void f(int i, int j) {\n" " if (i == j) {} else {\n" " if (i != j) {}\n" " }\n" "}\n"; ASSERT_EQUALS(true, valueOfTok(code, "!=").intvalue == 1); code = "void f(bool b, int i, int j) {\n" " if (b && i == j) {\n" " if (i != j) {}\n" " }\n" "}\n"; ASSERT_EQUALS(true, valueOfTok(code, "!=").intvalue == 0); code = "void f(bool b, int i, int j) {\n" " if (b || i == j) {\n" " if (i != j) {}\n" " }\n" "}\n"; ASSERT_EQUALS(true, tokenValues(code, "!=").empty()); code = "void f(bool b, int i, int j) {\n" " if (b || i == j) {} else {\n" " if (i != j) {}\n" " }\n" "}\n"; ASSERT_EQUALS(true, valueOfTok(code, "!=").intvalue == 1); code = "void f(bool b, int i, int j) {\n" " if (b && i == j) {} else {\n" " if (i != j) {}\n" " }\n" "}\n"; ASSERT_EQUALS(true, tokenValues(code, "!=").empty()); code = "void foo()\n" // #8924 "{\n" " if ( this->FileIndex >= 0 )\n" " return;\n" "\n" " this->FileIndex = 1 ;\n" " if ( this->FileIndex < 0 ) {}\n" "}"; ASSERT_EQUALS(false, valueOfTok(code, "<").intvalue == 1); code = "int f(int p) {\n" " int v = 0;\n" " for (int i = 0; i < 1; ++i) {\n" " if (p == 0)\n" " v = 1;\n" " if (v == 1)\n" " break;\n" " }\n" " int x = v;\n" " return x;\n" "}\n"; ASSERT_EQUALS(false, testValueOfXKnown(code, 10U, 0)); ASSERT_EQUALS(false, testValueOfXKnown(code, 10U, 1)); code = "void f() {\n" " const int size = arrayInfo.num(0);\n" " if (size <= 0)\n" " return;\n" " for (;;)\n" " if (size > 0) {}\n" "}\n"; ASSERT_EQUALS(true, valueOfTok(code, "> 0").isKnown()); ASSERT_EQUALS(true, valueOfTok(code, "> 0").intvalue == 1); // FP #10110 code = "enum FENUMS { NONE = 0, CB = 8 };\n" "bool calc(int x) {\n" " if (!x) {\n" " return false;\n" " }\n" "\n" " if (x & CB) {\n" " return true;\n" " }\n" " return false;\n" "}\n"; ASSERT_EQUALS(false, valueOfTok(code, "& CB").isKnown()); ASSERT_EQUALS(true, testValueOfXImpossible(code, 7U, 0)); code = "enum FENUMS { NONE = 0, CB = 8 };\n" "bool calc(int x) {\n" " if (x) {\n" " return false;\n" " }\n" "\n" " if ((!x) & CB) {\n" " return true;\n" " }\n" " return false;\n" "}\n"; ASSERT_EQUALS(true, valueOfTok(code, "& CB").isKnown()); ASSERT_EQUALS(true, testValueOfXKnown(code, 7U, 0)); code = "enum FENUMS { NONE = 0, CB = 8 };\n" "bool calc(int x) {\n" " if (!!x) {\n" " return false;\n" " }\n" "\n" " if (x & CB) {\n" " return true;\n" " }\n" " return false;\n" "}\n"; ASSERT_EQUALS(true, valueOfTok(code, "& CB").isKnown()); ASSERT_EQUALS(true, testValueOfXKnown(code, 7U, 0)); code = "bool calc(bool x) {\n" " if (!x) {\n" " return false;\n" " }\n" "\n" " if (x) {\n" " return true;\n" " }\n" " return false;\n" "}\n"; ASSERT_EQUALS(true, testValueOfXKnown(code, 6U, 1)); code = "bool calc(bool x) {\n" " if (x) {\n" " return false;\n" " }\n" "\n" " if (!x) {\n" " return true;\n" " }\n" " return false;\n" "}\n"; ASSERT_EQUALS(true, testValueOfXKnown(code, 6U, 0)); } static std::string isPossibleContainerSizeValue(const std::list &values, MathLib::bigint i) { if (values.size() != 1) return "values.size():" + std::to_string(values.size()); if (!values.front().isContainerSizeValue()) return "ContainerSizeValue"; if (!values.front().isPossible()) return "Possible"; if (values.front().intvalue != i) return "intvalue:" + std::to_string(values.front().intvalue); return ""; } static std::string isImpossibleContainerSizeValue(const std::list& values, MathLib::bigint i) { if (values.size() != 1) return "values.size():" + std::to_string(values.size()); if (!values.front().isContainerSizeValue()) return "ContainerSizeValue"; if (!values.front().isImpossible()) return "Impossible"; if (values.front().intvalue != i) return "intvalue:" + std::to_string(values.front().intvalue); return ""; } static std::string isInconclusiveContainerSizeValue(const std::list& values, MathLib::bigint i) { if (values.size() != 1) return "values.size():" + std::to_string(values.size()); if (!values.front().isContainerSizeValue()) return "ContainerSizeValue"; if (!values.front().isInconclusive()) return "Inconclusive"; if (values.front().intvalue != i) return "intvalue:" + std::to_string(values.front().intvalue); return ""; } static std::string isKnownContainerSizeValue(const std::list &values, MathLib::bigint i) { if (values.size() != 1) return "values.size():" + std::to_string(values.size()); if (!values.front().isContainerSizeValue()) return "ContainerSizeValue"; if (!values.front().isKnown()) return "Known"; if (values.front().intvalue != i) return "intvalue:" + std::to_string(values.front().intvalue); return ""; } void valueFlowContainerSize() { const char *code; LOAD_LIB_2(settings.library, "std.cfg"); // condition code = "void f(const std::list &ints) {\n" " if (!static_cast(ints.empty()))\n" " ints.front();\n" "}"; ASSERT_EQUALS("", isImpossibleContainerSizeValue(tokenValues(code, "ints . front"), 0)); // valueFlowContainerReverse code = "void f(const std::list &ints) {\n" " ints.front();\n" // <- container can be empty " if (ints.empty()) {}\n" "}"; ASSERT_EQUALS("", isPossibleContainerSizeValue(tokenValues(code, "ints . front"), 0)); code = "void f(const std::list &ints) {\n" " ints.front();\n" // <- container can be empty " if (ints.size()==0) {}\n" "}"; ASSERT_EQUALS("", isPossibleContainerSizeValue(tokenValues(code, "ints . front"), 0)); code = "void f(std::list ints) {\n" " ints.front();\n" // <- no container size " ints.pop_back();\n" " if (ints.empty()) {}\n" "}"; ASSERT(tokenValues(code, "ints . front").empty()); code = "void f(std::vector v) {\n" " v[10] = 0;\n" // <- container size can be 10 " if (v.size() == 10) {}\n" "}"; ASSERT_EQUALS("", isPossibleContainerSizeValue(tokenValues(code, "v ["), 10)); code = "void f(std::vector params) {\n" " switch(x) {\n" " case CMD_RESPONSE:\n" " if(y) { break; }\n" " params[2];\n" // <- container use " break;\n" " case CMD_DELETE:\n" " if (params.size() < 2) { }\n" // <- condition " break;\n" " }\n" "}"; ASSERT(tokenValues(code, "params [ 2 ]").empty()); // valueFlowAfterCondition code = "void f(const std::vector& v) {\n" " if(v.empty()) {\n" " v.front();\n" " }\n" "}\n"; ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "v . front"), 0)); code = "void f(const std::vector& v) {\n" " if(!v.empty()) {\n" " v.front();\n" " }\n" "}\n"; ASSERT_EQUALS("", isImpossibleContainerSizeValue(tokenValues(code, "v . front"), 0)); code = "void f(const std::vector& v) {\n" " if(!v.empty() && v[0] != \"\") {\n" " v.front();\n" " }\n" "}\n"; ASSERT_EQUALS("", isImpossibleContainerSizeValue(tokenValues(code, "v . front"), 0)); // valueFlowContainerForward code = "void f(const std::list &ints) {\n" " if (ints.empty()) {}\n" " ints.front();\n" // <- container can be empty "}"; ASSERT_EQUALS("", isPossibleContainerSizeValue(tokenValues(code, "ints . front"), 0)); code = "void f(const std::list &ints) {\n" " if (ints.empty()) { continue; }\n" " ints.front();\n" // <- no container size "}"; ASSERT_EQUALS("", isImpossibleContainerSizeValue(tokenValues(code, "ints . front"), 0)); code = "void f(const std::list &ints) {\n" " if (ints.empty()) { ints.push_back(0); }\n" " ints.front();\n" // <- container is not empty "}"; ASSERT(tokenValues(code, "ints . front").empty()); code = "void f(const std::list &ints) {\n" " if (ints.empty()) {\n" " ints.front();\n" // <- container is empty " }\n" "}"; ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "ints . front"), 0)); code = "void f(const std::list &ints) {\n" " if (ints.size() == 3) {\n" " ints.front();\n" // <- container size is 3 " }\n" "}"; ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "ints . front"), 3)); code = "void f(const std::list &ints) {\n" " if (ints.size() <= 3) {\n" " ints.front();\n" // <- container size is 3 " }\n" "}"; ASSERT_EQUALS("", isPossibleContainerSizeValue(tokenValues(code, "ints . front"), 3)); code = "void f(const std::list &ints) {\n" " if (ints.size() >= 3) {\n" " ints.front();\n" // <- container size is 3 " }\n" "}"; ASSERT_EQUALS("", isPossibleContainerSizeValue(tokenValues(code, "ints . front"), 3)); code = "void f(const std::list &ints) {\n" " if (ints.size() < 3) {\n" " ints.front();\n" // <- container size is 2 " }\n" "}"; ASSERT_EQUALS("", isPossibleContainerSizeValue(tokenValues(code, "ints . front"), 2)); code = "void f(const std::list &ints) {\n" " if (ints.size() > 3) {\n" " ints.front();\n" // <- container size is 4 " }\n" "}"; ASSERT_EQUALS("", isPossibleContainerSizeValue(tokenValues(code, "ints . front"), 4)); code = "void f(const std::list &ints) {\n" " if (ints.empty() == false) {\n" " ints.front();\n" // <- container is not empty " }\n" "}"; ASSERT(tokenValues(code, "ints . front").empty()); code = "void f(const std::vector &v) {\n" " if (v.empty()) {}\n" " if (!v.empty() && v[10]==0) {}\n" // <- no container size for 'v[10]' "}"; ASSERT(tokenValues(code, "v [").empty()); code = "void f() {\n" " std::list ints;\n" // No value => ints is empty " ints.front();\n" "}"; ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "ints . front"), 0)); code = "void f() {\n" " std::array ints;\n" // Array size is 10 " ints.front();\n" "}"; ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "ints . front"), 10)); code = "void f() {\n" " std::string s;\n" " cin >> s;\n" " s[0];\n" "}"; ASSERT(tokenValues(code, "s [").empty()); code = "void f() {\n" " std::string s = \"abc\";\n" // size of s is 3 " s.size();\n" "}"; ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "s . size"), 3)); code = "void f() {\n" " std::string s=\"abc\";\n" // size of s is 3 " s += unknown;\n" " s.size();\n" "}"; ASSERT(tokenValues(code, "s . size").empty()); code = "void f() {\n" " std::string s=\"abc\";\n" // size of s is 3 " s += \"def\";\n" // size of s => 6 " s.size();\n" "}"; ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "s . size"), 6)); code = "void f(std::string s) {\n" " if (s == \"hello\")\n" " s[40] = c;\n" "}"; ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "s ["), 5)); code = "void f(std::string s) {\n" " s[40] = c;\n" " if (s == \"hello\") {}\n" "}"; ASSERT_EQUALS("", isPossibleContainerSizeValue(tokenValues(code, "s ["), 5)); code = "void f(std::string s) {\n" " if (s != \"hello\") {}\n" " s[40] = c;\n" "}"; ASSERT_EQUALS("", isPossibleContainerSizeValue(tokenValues(code, "s ["), 5)); code = "void f(std::string s) {\n" " if (s != \"hello\")\n" " s[40] = c;\n" "}"; ASSERT_EQUALS("", isImpossibleContainerSizeValue(tokenValues(code, "s ["), 5)); code = "void f() {\n" " static std::string s;\n" " if (s.size() == 0)\n" " s = x;\n" "}"; ASSERT(tokenValues(code, "s . size").empty()); // valueFlowContainerForward, loop code = "void f() {\n" " std::stack links;\n" " while (!links.empty() || indentlevel)\n" " links.push(tok);\n" "}"; ASSERT_EQUALS("", isPossibleContainerSizeValue(tokenValues(code, "links . empty"), 0)); // valueFlowContainerForward, function call code = "void f() {\n" " std::list x;\n" " f(x);\n" " x.front();\n" // <- unknown container size "}"; ASSERT(tokenValues(code, "x . front").empty()); code = "void f() {\n" // #8689 " std::list x;\n" " f(x);\n" " x.front();\n" // <- unknown container size "}"; ASSERT(tokenValues(code, "x . front").empty()); code = "void g(std::list&);\n" "void f() {\n" " std::list x;\n" " g(x);\n" " x.front();\n" "}"; ASSERT(tokenValues(code, "x . front").empty()); code = "void g(std::list*);\n" "void f() {\n" " std::list x;\n" " g(&x);\n" " x.front();\n" "}"; ASSERT(tokenValues(code, "x . front").empty()); code = "void g(std::list* const);\n" // #9434 "void f() {\n" " std::list x;\n" " g(&x);\n" " x.front();\n" "}"; ASSERT(tokenValues(code, "x . front").empty()); code = "void g(const std::list&);\n" "void f() {\n" " std::list x;\n" " g(x);\n" " x.front();\n" "}"; ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "x . front"), 0)); code = "void g(std::list);\n" "void f() {\n" " std::list x;\n" " g(x);\n" " x.front();\n" "}"; ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "x . front"), 0)); code = "void g(int&);\n" "void f() {\n" " std::list x;\n" " g(x[0]);\n" " x.front();\n" "}"; ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "x . front"), 0)); code = "void g(int&);\n" "void f() {\n" " std::list x;\n" " g(x.back());\n" " x.front();\n" "}"; ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "x . front"), 0)); code = "void g(std::list&) {}\n" "void f() {\n" " std::list x;\n" " g(x);\n" " x.front();\n" "}"; ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "x . front"), 0)); code = "void g(std::list& y) { y.push_back(1); }\n" "void f() {\n" " std::list x;\n" " g(x);\n" " x.front();\n" "}"; ASSERT(tokenValues(code, "x . front").empty()); code = "void g(std::list*) {}\n" "void f() {\n" " std::list x;\n" " g(&x);\n" " x.front();\n" "}"; ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "x . front"), 0)); code = "void g(std::list* y) { y->push_back(1); }\n" "void f() {\n" " std::list x;\n" " g(&x);\n" " x.front();\n" "}"; ASSERT(tokenValues(code, "x . front").empty()); code = "void h(std::list&);\n" "void g(std::list& y) { h(y); }\n" "void f() {\n" " std::list x;\n" " g(x);\n" " x.front();\n" "}"; ASSERT(tokenValues(code, "x . front").empty()); code = "void h(const std::list&);\n" "void g(std::list& y) { h(y); }\n" "void f() {\n" " std::list x;\n" " g(x);\n" " x.front();\n" "}"; ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "x . front"), 0)); code = "void h(const std::list&);\n" "void g(std::list& y) { h(y); y.push_back(1); }\n" "void f() {\n" " std::list x;\n" " g(x);\n" " x.front();\n" "}"; ASSERT(tokenValues(code, "x . front").empty()); code = "void f(std::vector ints) {\n" // #8697 " if (ints.empty())\n" " abort() << 123;\n" " ints[0] = 0;\n" "}"; ASSERT_EQUALS("", isImpossibleContainerSizeValue(tokenValues(code, "ints ["), 0)); code = "struct A {\n" // forward, nested function call, #9424 " double getMessage( std::vector *message );\n" "};\n" "\n" "struct B {\n" " A *a;\n" " double getMessage( std::vector *message ) { return a->getMessage( message ); }\n" "};\n" "\n" "void foo(B *ptr) {\n" " std::vector v;\n" " ptr->getMessage (&v);\n" " if (v.size () > 0) {}\n" // <- v has unknown size! "}"; ASSERT_EQUALS(0U, tokenValues(code, "v . size ( )").size()); // if code = "bool f(std::vector&) {\n" // #9532 " return false;\n" "}\n" "int g() {\n" " std::vector v;\n" " if (f(v) || v.empty())\n" " return 0;\n" " return v[0];\n" "}\n"; ASSERT_EQUALS(0U, tokenValues(code, "v [ 0 ]").size()); // container size => yields code = "void f() {\n" " std::string s = \"abcd\";\n" " s.size();\n" "}"; ASSERT_EQUALS(4, tokenValues(code, "( ) ;").front().intvalue); code = "void f() {\n" " std::string s;\n" " s.empty();\n" "}"; ASSERT_EQUALS(1, tokenValues(code, "( ) ;").front().intvalue); // Calculations code = "void f() {\n" " std::string s = \"abcd\";\n" " x = s + s;\n" "}"; ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "+"), 8)); code = "void f(const std::vector &ints) {\n" " ints.clear();\n" " ints.front();\n" "}"; ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "ints . front", ValueFlow::Value::ValueType::CONTAINER_SIZE), 0)); code = "void f(const std::vector &ints) {\n" " ints.resize(3);\n" " ints.front();\n" "}"; ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "ints . front", ValueFlow::Value::ValueType::CONTAINER_SIZE), 3)); code = "void f(const std::vector &ints) {\n" " ints.resize(3);\n" " ints.push_back(3);\n" " ints.front();\n" "}"; ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "ints . front", ValueFlow::Value::ValueType::CONTAINER_SIZE), 4)); code = "void f(const std::vector &ints) {\n" " ints.resize(3);\n" " ints.pop_back();\n" " ints.front();\n" "}"; ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "ints . front", ValueFlow::Value::ValueType::CONTAINER_SIZE), 2)); code = "int f(bool b) {\n" " std::map m;\n" " if (b)\n" " m[0] = 1;\n" " return m.at(0);\n" "}\n"; ASSERT_EQUALS("", isPossibleContainerSizeValue(tokenValues(code, "m . at", ValueFlow::Value::ValueType::CONTAINER_SIZE), 0)); code = "struct Base {\n" " virtual bool GetString(std::string &) const { return false; }\n" "};\n" "int f() {\n" " std::string str;\n" " Base *b = GetClass();\n" " if (!b->GetString(str)) {\n" " return -2;\n" " }\n" " else {\n" " return str.front();\n" " }\n" "}\n"; ASSERT_EQUALS(0U, tokenValues(code, "str . front").size()); code = "void f() {\n" " std::vector ints{};\n" " ints.front();\n" "}"; ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "ints . front", ValueFlow::Value::ValueType::CONTAINER_SIZE), 0)); code = "void f() {\n" " std::vector ints{1};\n" " ints.front();\n" "}"; ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "ints . front", ValueFlow::Value::ValueType::CONTAINER_SIZE), 1)); code = "void f() {\n" " std::vector ints{1};\n" " std::vector ints2{ints.begin(), ints.end()};\n" " ints2.front();\n" "}"; ASSERT_EQUALS( "", isKnownContainerSizeValue(tokenValues(code, "ints2 . front", ValueFlow::Value::ValueType::CONTAINER_SIZE), 1)); code = "void f() {\n" " std::vector ints = {};\n" " ints.front();\n" "}"; ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "ints . front", ValueFlow::Value::ValueType::CONTAINER_SIZE), 0)); code = "void f() {\n" " std::vector ints = {1};\n" " ints.front();\n" "}"; ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "ints . front", ValueFlow::Value::ValueType::CONTAINER_SIZE), 1)); code = "void f(std::string str) {\n" " if (str == \"123\")\n" " bool x = str.empty();\n" "}\n"; ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "str . empty", ValueFlow::Value::ValueType::CONTAINER_SIZE), 3)); code = "void f(std::string str) {\n" " if (str == \"123\") {\n" " bool x = (str == \"\");\n" " bool a = x;\n" " }\n" "}\n"; ASSERT_EQUALS(true, testValueOfXKnown(code, 4U, 0)); code = "void f(std::string str) {\n" " if (str == \"123\") {\n" " bool x = (str != \"\");\n" " bool a = x;\n" " }\n" "}\n"; ASSERT_EQUALS(true, testValueOfXKnown(code, 4U, 1)); code = "void f(std::string str) {\n" " if (str == \"123\") {\n" " bool x = (str == \"321\");\n" " bool a = x;\n" " }\n" "}\n"; ASSERT_EQUALS(false, testValueOfXKnown(code, 4U, 1)); code = "void f(std::string str) {\n" " if (str == \"123\") {\n" " bool x = (str != \"321\");\n" " bool a = x;\n" " }\n" "}\n"; ASSERT_EQUALS(false, testValueOfXKnown(code, 4U, 0)); code = "void f(std::string str) {\n" " if (str.size() == 1) {\n" " bool x = (str == \"123\");\n" " bool a = x;\n" " }\n" "}\n"; ASSERT_EQUALS(true, testValueOfXKnown(code, 4U, 0)); code = "bool f(std::string s) {\n" " if (!s.empty()) {\n" " bool x = s == \"0\";\n" " return x;\n" " }\n" " return false;\n" "}\n"; ASSERT_EQUALS(false, testValueOfXKnown(code, 4U, 0)); ASSERT_EQUALS(false, testValueOfXKnown(code, 4U, 1)); ASSERT_EQUALS(false, testValueOfXImpossible(code, 4U, 0)); code = "bool f() {\n" " std::list x1;\n" " std::list x2;\n" " for (int i = 0; i < 10; ++i) {\n" " std::list& x = (i < 5) ? x1 : x2;\n" " x.push_back(i);\n" " }\n" " return x1.empty() || x2.empty();\n" "}\n"; ASSERT_EQUALS("", isInconclusiveContainerSizeValue(tokenValues(code, "x1 . empty", ValueFlow::Value::ValueType::CONTAINER_SIZE), 0)); ASSERT_EQUALS("", isInconclusiveContainerSizeValue(tokenValues(code, "x2 . empty", ValueFlow::Value::ValueType::CONTAINER_SIZE), 0)); code = "std::vector g();\n" "int f(bool b) {\n" " std::set a;\n" " std::vector c = g();\n" " a.insert(c.begin(), c.end());\n" " return a.size();\n" "}\n"; ASSERT_EQUALS(true, tokenValues(code, "a . size", ValueFlow::Value::ValueType::CONTAINER_SIZE).empty()); code = "std::vector g();\n" "std::vector f() {\n" " std::vector v = g();\n" " if (!v.empty()) {\n" " if (v[0] != 0)\n" " v.clear();\n" " }\n" " if (!v.empty() && v[0] != 0) {}\n" " return v;\n" "}\n"; ASSERT_EQUALS(true, tokenValues(code, "v [ 0 ] != 0 ) { }", ValueFlow::Value::ValueType::CONTAINER_SIZE).empty()); } void valueFlowDynamicBufferSize() { const char *code; LOAD_LIB_2(settings.library, "std.cfg"); LOAD_LIB_2(settings.library, "posix.cfg"); code = "void* f() {\n" " void* x = malloc(10);\n" " return x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 10, ValueFlow::Value::ValueType::BUFFER_SIZE)); code = "void* f() {\n" " void* x = calloc(4, 5);\n" " return x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 3U, 20, ValueFlow::Value::ValueType::BUFFER_SIZE)); code = "void* f() {\n" " const char* y = \"abcd\";\n" " const char* x = strdup(y);\n" " return x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 5, ValueFlow::Value::ValueType::BUFFER_SIZE)); code = "void* f() {\n" " void* y = malloc(10);\n" " void* x = realloc(y, 20);\n" " return x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 20, ValueFlow::Value::ValueType::BUFFER_SIZE)); code = "void* f() {\n" " void* y = calloc(10, 4);\n" " void* x = reallocarray(y, 20, 5);\n" " return x;\n" "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 100, ValueFlow::Value::ValueType::BUFFER_SIZE)); } void valueFlowSafeFunctionParameterValues() { const char *code; std::list values; Settings s; LOAD_LIB_2(s.library, "std.cfg"); s.safeChecks.classes = s.safeChecks.externalFunctions = s.safeChecks.internalFunctions = true; code = "short f(short x) {\n" " return x + 0;\n" "}"; values = tokenValues(code, "+", &s); ASSERT_EQUALS(2, values.size()); ASSERT_EQUALS(-0x8000, values.front().intvalue); ASSERT_EQUALS(0x7fff, values.back().intvalue); code = "short f(std::string x) {\n" " return x[10];\n" "}"; values = tokenValues(code, "x [", &s); ASSERT_EQUALS(2, values.size()); ASSERT_EQUALS(0, values.front().intvalue); ASSERT_EQUALS(1000000, values.back().intvalue); code = "int f(float x) {\n" " return x;\n" "}"; values = tokenValues(code, "x ;", &s); ASSERT_EQUALS(2, values.size()); ASSERT(values.front().floatValue < -1E20); ASSERT(values.back().floatValue > 1E20); code = "short f(__cppcheck_low__(0) __cppcheck_high__(100) short x) {\n" " return x + 0;\n" "}"; values = tokenValues(code, "+", &s); ASSERT_EQUALS(2, values.size()); ASSERT_EQUALS(0, values.front().intvalue); ASSERT_EQUALS(100, values.back().intvalue); code = "unsigned short f(unsigned short x) [[expects: x <= 100]] {\n" " return x + 0;\n" "}"; values = tokenValues(code, "+", &s); ASSERT_EQUALS(2, values.size()); ASSERT_EQUALS(0, values.front().intvalue); ASSERT_EQUALS(100, values.back().intvalue); } void valueFlowUnknownFunctionReturn() { const char *code; std::list values; Settings s; LOAD_LIB_2(s.library, "std.cfg"); s.checkUnknownFunctionReturn.insert("rand"); code = "x = rand();"; values = tokenValues(code, "(", &s); ASSERT_EQUALS(2, values.size()); ASSERT_EQUALS(INT_MIN, values.front().intvalue); ASSERT_EQUALS(INT_MAX, values.back().intvalue); } void valueFlowPointerAliasDeref() { const char* code; code = "int f() {\n" " int a = 123;\n" " int *p = &a;\n" " int x = *p;\n" " return x;\n" "}\n"; ASSERT_EQUALS(true, testValueOfX(code, 5U, 123)); } void valueFlowCrashIncompleteCode() { const char* code; code = "void SlopeFloor::setAttr(const Value &val) {\n" " int x = val;\n" " if (x >= -1)\n" " state = x;\n" "}\n"; valueOfTok(code, "="); code = "void a() {\n" " auto b = [b = 0] {\n" " if (b) {\n" " }\n" " };\n" "}\n"; valueOfTok(code, "0"); } void valueFlowCrash() { const char* code; code = "void f(int x) {\n" " if (0 * (x > 2)) {}\n" "}\n"; valueOfTok(code, "x"); code = "const int& f(int, const int& y = 0);\n" "const int& f(int, const int& y) {\n" " return y;\n" "}\n" "const int& g(int x) {\n" " const int& r = f(x);\n" " return r;\n" "}\n"; valueOfTok(code, "0"); code = "void fa(int &colors) {\n" " for (int i = 0; i != 6; ++i) {}\n" "}\n" "void fb(not_null parent, int &&colors2) {\n" " dostuff(1);\n" "}\n"; valueOfTok(code, "x"); code = "void a() {\n" " static int x = 0;\n" " struct c {\n" " c(c &&) { ++x; }\n" " };\n" "}\n"; valueOfTok(code, "x"); code = "void f(){\n" " struct dwarf_data **pp;\n" " for (pp = (struct dwarf_data **) (void *) &state->fileline_data;\n" " *pp != NULL;\n" " pp = &(*pp)->next)\n" " ;\n" "}\n"; valueOfTok(code, "x"); code = "void *foo(void *x);\n" "void *foo(void *x)\n" "{\n" " if (!x)\n" "yes:\n" " return &&yes;\n" " return x;\n" "}\n"; valueOfTok(code, "x"); code = "void f() {\n" " std::string a = b[c->d()];\n" " if(a.empty()) {\n" " INFO(std::string{\"a\"} + c->d());\n" " INFO(std::string{\"b\"} + a);\n" " }\n" "}\n"; valueOfTok(code, "a"); code = "class A{\n" " void f() {\n" " std::string c{s()};\n" " }\n" " std::string s() {\n" " return \"\";\n" " }\n" "};\n"; valueOfTok(code, "c"); code = "void f() {\n" " char* p = 0;\n" " int pi =\n" " p == \"a\" ? 1 : p == \"a\" ? 1 : p == \"a\" ? 1 : p == \"a\" ? 1 : p == \"a\" ? 1 \n" " : p == \"a\" ? 1 : p == \"a\" ? 1 : p == \"a\" ? 1 : p == \"a\" ? 1 : p == \"a\" ? 1 \n" " : p == \"a\" ? 1 : p == \"a\" ? 1 : p == \"a\" ? 1 : p == \"a\" ? 1 : p == \"a\" ? 1 \n" " : p == \"a\" ? 1 : p == \"a\" ? 1 : p == \"a\" ? 1 : p == \"a\" ? 1 : p == \"a\" ? 1 \n" " : p == \"a\" ? 1 : p == \"a\" ? 1 : p == \"a\" ? 1 : p == \"a\" ? 1 : p == \"a\" ? 1 \n" " : 0;\n" " int *i2 = 0;\n" " if (i2) { }\n" "}\n"; valueOfTok(code, "p"); code = "struct a;\n" "namespace e {\n" "struct f {\n" " struct g {\n" " enum {} h;\n" " int arg;\n" " };\n" " std::vector i;\n" "};\n" "} // namespace e\n" "void fn1() {\n" " std::vector arguments;\n" " e::f b;\n" " for (e::f::g c : b.i)\n" " if (c.h)\n" " a *const d = arguments[c.arg];\n" "}\n"; valueOfTok(code, "c"); } void valueFlowHang() { const char* code; // #9659 code = "float arr1[4][4] = {0.0};\n" "float arr2[4][4] = {0.0};\n" "void f() {\n" " if(arr1[0][0] == 0.0 &&\n" " arr1[0][1] == 0.0 &&\n" " arr1[0][2] == 0.0 &&\n" " arr1[0][3] == 0.0 &&\n" " arr1[1][0] == 0.0 &&\n" " arr1[1][1] == 0.0 &&\n" " arr1[1][2] == 0.0 &&\n" " arr1[1][3] == 0.0 &&\n" " arr1[2][0] == 0.0 &&\n" " arr1[2][1] == 0.0 &&\n" " arr1[2][2] == 0.0 &&\n" " arr1[2][3] == 0.0 &&\n" " arr1[3][0] == 0.0 &&\n" " arr1[3][1] == 0.0 &&\n" " arr1[3][2] == 0.0 &&\n" " arr1[3][3] == 0.0 &&\n" " arr2[0][0] == 0.0 &&\n" " arr2[0][1] == 0.0 &&\n" " arr2[0][2] == 0.0 &&\n" " arr2[0][3] == 0.0 &&\n" " arr2[1][0] == 0.0 &&\n" " arr2[1][1] == 0.0 &&\n" " arr2[1][2] == 0.0 &&\n" " arr2[1][3] == 0.0 &&\n" " arr2[2][0] == 0.0 &&\n" " arr2[2][1] == 0.0 &&\n" " arr2[2][2] == 0.0 &&\n" " arr2[2][3] == 0.0 &&\n" " arr2[3][0] == 0.0 &&\n" " arr2[3][1] == 0.0 &&\n" " arr2[3][2] == 0.0 &&\n" " arr2[3][3] == 0.0\n" " ) {}\n" "}\n"; valueOfTok(code, "x"); code = "namespace {\n" "struct a {\n" " a(...) {}\n" " a(std::initializer_list>>>) {}\n" "} b{{0, {{&b, &b, &b, &b}}},\n" " {0,\n" " {{&b, &b, &b, &b, &b, &b, &b, &b, &b, &b},\n" " {{&b, &b, &b, &b, &b, &b, &b}}}},\n" " {0,\n" " {{&b, &b, &b, &b, &b, &b, &b, &b, &b, &b, &b, &b, &b, &b},\n" " {&b, &b, &b, &b, &b, &b, &b, &b, &b, &b, &b}}}};\n" "}\n"; valueOfTok(code, "x"); code = "namespace {\n" "struct a {\n" " a(...) {}\n" " a(std::initializer_list>>>) {}\n" "} b{{0, {{&b}}},\n" " {0, {{&b}}},\n" " {0, {{&b}}},\n" " {0, {{&b}}},\n" " {0, {{&b}, {&b, &b, &b, &b, &b, &b, &b, &b, &b, &b, {&b}}}},\n" " {0,\n" " {{&b},\n" " {&b, &b, &b, &b, &b, &b, &b, &b, &b, &b, &b, &b, &b, &b, &b, &b, &b, &b,\n" " &b}}}};\n" "}\n"; valueOfTok(code, "x"); } void valueFlowCrashConstructorInitialization() { // #9577 const char* code; code = "void Error()\n" "{\n" " VfsPath path(\"\");\n" " path = path / amtype;\n" " size_t base = 0;\n" " VfsPath standard(\"standard\");\n" " if (path != standard)\n" " {\n" " }\n" "}"; valueOfTok(code, "path"); code = "void Error()\n" "{\n" " VfsPath path;\n" " path = path / amtype;\n" " size_t base = 0;\n" " VfsPath standard(\"standard\");\n" " if (path != standard)\n" " {\n" " }\n" "}"; valueOfTok(code, "path"); } void valueFlowUnknownMixedOperators() { const char *code= "int f(int a, int b, bool x) {\n" " if (a == 1 && (!(b == 2 && x))) {\n" " } else {\n" " if (x) {\n" " }\n" " }\n" "\n" " return 0;\n" "}" ; ASSERT_EQUALS(false, testValueOfXKnown(code, 4U, 1)); } void valueFlowIdempotent() { const char *code; code = "void f(bool a, bool b) {\n" " bool x = true;\n" " if (a)\n" " x = x && b;\n" " bool result = x;\n" "}\n"; ASSERT_EQUALS(false, testValueOfXKnown(code, 5U, 1)); code = "void f(bool a, bool b) {\n" " bool x = false;\n" " if (a)\n" " x = x && b;\n" " bool result = x;\n" "}\n"; ASSERT_EQUALS(true, testValueOfXKnown(code, 5U, 0)); code = "void f(bool a, bool b) {\n" " bool x = true;\n" " if (a)\n" " x = x || b;\n" " bool result = x;\n" "}\n"; ASSERT_EQUALS(true, testValueOfXKnown(code, 5U, 1)); code = "void f(bool a, bool b) {\n" " bool x = false;\n" " if (a)\n" " x = x || b;\n" " bool result = x;\n" "}\n"; ASSERT_EQUALS(false, testValueOfXKnown(code, 5U, 0)); } }; REGISTER_TEST(TestValueFlow)