[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/cppcheck-opensource/cppcheck/2.13.x/test/testvalueflow.cpp [Back]  [Original]

/*
 * Cppcheck - A tool for static C/C++ code analysis
 * Copyright (C) 2007-2023 Cppcheck team.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 */

#include "helpers.h"
#include "mathlib.h"
#include "platform.h"
#include "settings.h"
#include "fixture.h"
#include "token.h"
#include "tokenize.h"
#include "vfvalue.h"

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

class TestValueFlow : public TestFixture {
public:
    TestValueFlow() : TestFixture("TestValueFlow") {}

private:
    Settings settings = settingsBuilder().library("std.cfg").build();

    void run() override {
        // strcpy, abort cfg
        constexpr char cfg[] = "\n"
                               "\n"
                               "    \n"
                               "   true \n" // abort is a noreturn function
                               "";
        settings = settingsBuilder(settings).libraryxml(cfg, sizeof(cfg)).build();

        TEST_CASE(valueFlowNumber);
        TEST_CASE(valueFlowString);
        TEST_CASE(valueFlowPointerAlias);
        TEST_CASE(valueFlowLifetime);
        TEST_CASE(valueFlowArrayElement);
        TEST_CASE(valueFlowMove);

        TEST_CASE(valueFlowBitAnd);
        TEST_CASE(valueFlowRightShift);

        TEST_CASE(valueFlowCalculations);
        TEST_CASE(valueFlowSizeof);
        TEST_CASE(valueFlowComma);

        TEST_CASE(valueFlowErrorPath);

        TEST_CASE(valueFlowBeforeCondition);
        TEST_CASE(valueFlowBeforeConditionAndAndOrOrGuard);
        TEST_CASE(valueFlowBeforeConditionAssignIncDec);
        TEST_CASE(valueFlowBeforeConditionFunctionCall);
        TEST_CASE(valueFlowBeforeConditionGlobalVariables);
        TEST_CASE(valueFlowBeforeConditionGoto);
        TEST_CASE(valueFlowBeforeConditionIfElse);
        TEST_CASE(valueFlowBeforeConditionLoop);
        TEST_CASE(valueFlowBeforeConditionMacro);
        TEST_CASE(valueFlowBeforeConditionSizeof);
        TEST_CASE(valueFlowBeforeConditionSwitch);
        TEST_CASE(valueFlowBeforeConditionTernaryOp);
        TEST_CASE(valueFlowBeforeConditionForward);
        TEST_CASE(valueFlowBeforeConditionConstructor);

        TEST_CASE(valueFlowAfterAssign);
        TEST_CASE(valueFlowAfterSwap);
        TEST_CASE(valueFlowAfterCondition);
        TEST_CASE(valueFlowAfterConditionTernary);
        TEST_CASE(valueFlowAfterConditionExpr);
        TEST_CASE(valueFlowAfterConditionSeveralNot);
        TEST_CASE(valueFlowForwardCompoundAssign);
        TEST_CASE(valueFlowForwardCorrelatedVariables);
        TEST_CASE(valueFlowForwardModifiedVariables);
        TEST_CASE(valueFlowForwardFunction);
        TEST_CASE(valueFlowForwardTernary);
        TEST_CASE(valueFlowForwardLambda);
        TEST_CASE(valueFlowForwardTryCatch);
        TEST_CASE(valueFlowForwardInconclusiveImpossible);
        TEST_CASE(valueFlowForwardConst);
        TEST_CASE(valueFlowForwardAfterCondition);

        TEST_CASE(valueFlowFwdAnalysis);

        TEST_CASE(valueFlowSwitchVariable);

        TEST_CASE(valueFlowForLoop);
        TEST_CASE(valueFlowSubFunction);
        TEST_CASE(valueFlowFunctionReturn);

        TEST_CASE(valueFlowFunctionDefaultParameter);

        TEST_CASE(knownValue);

        TEST_CASE(valueFlowSizeofForwardDeclaredEnum);

        TEST_CASE(valueFlowGlobalVar);

        TEST_CASE(valueFlowGlobalConstVar);

        TEST_CASE(valueFlowGlobalStaticVar);

        TEST_CASE(valueFlowInlineAssembly);

        TEST_CASE(valueFlowSameExpression);

        TEST_CASE(valueFlowUninit);

        TEST_CASE(valueFlowConditionExpressions);

        TEST_CASE(valueFlowContainerSize);
        TEST_CASE(valueFlowContainerElement);

        TEST_CASE(valueFlowDynamicBufferSize);

        TEST_CASE(valueFlowSafeFunctionParameterValues);
        TEST_CASE(valueFlowUnknownFunctionReturn);

        TEST_CASE(valueFlowPointerAliasDeref);

        TEST_CASE(valueFlowCrashIncompleteCode);

        TEST_CASE(valueFlowCrash);
        TEST_CASE(valueFlowHang);
        TEST_CASE(valueFlowCrashConstructorInitialization);

        TEST_CASE(valueFlowUnknownMixedOperators);
        TEST_CASE(valueFlowSolveExpr);
        TEST_CASE(valueFlowIdempotent);
        TEST_CASE(valueFlowUnsigned);
        TEST_CASE(valueFlowMod);
        TEST_CASE(valueFlowNotNull);
        TEST_CASE(valueFlowSymbolic);
        TEST_CASE(valueFlowSymbolicIdentity);
        TEST_CASE(valueFlowSymbolicStrlen);
        TEST_CASE(valueFlowSmartPointer);
        TEST_CASE(valueFlowImpossibleMinMax);
        TEST_CASE(valueFlowImpossibleIncDec);
        TEST_CASE(valueFlowImpossibleUnknownConstant);
        TEST_CASE(valueFlowContainerEqual);

        TEST_CASE(performanceIfCount);
    }

    static bool isNotTokValue(const ValueFlow::Value &val) {
        return !val.isTokValue();
    }

    // cppcheck-suppress unusedPrivateFunction
    static bool isNotLifetimeValue(const ValueFlow::Value& val) {
        return !val.isLifetimeValue();
    }

    static bool isNotUninitValue(const ValueFlow::Value& val) {
        return !val.isUninitValue();
    }

    static bool isNotPossible(const ValueFlow::Value& val) {
        return !val.isPossible();
    }

    static bool isNotKnown(const ValueFlow::Value& val) {
        return !val.isKnown();
    }

    static bool isNotInconclusive(const ValueFlow::Value& val) {
        return !val.isInconclusive();
    }

    static bool isNotImpossible(const ValueFlow::Value& val) {
        return !val.isImpossible();
    }

#define testValueOfXKnown(...) testValueOfXKnown_(__FILE__, __LINE__, __VA_ARGS__)
    bool testValueOfXKnown_(const char* file, int line, const char code[], unsigned int linenr, int value) {
        // Tokenize..
        Tokenizer tokenizer(&settings, this);
        std::istringstream istr(code);
        ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line);

        for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) {
            if (tok->str() == "x" && tok->linenr() == linenr) {
                if (std::any_of(tok->values().begin(), tok->values().end(), [&](const ValueFlow::Value& val) {
                    if (val.isSymbolicValue())
                        return false;
                    if (val.isKnown() && val.intvalue == value)
                        return true;
                    return false;
                }))
                    return true;
            }
        }

        return false;
    }

    bool testValueOfXKnown_(const char* file, int line, const char code[], unsigned int linenr, const std::string& expr, int value) {
        // Tokenize..
        Tokenizer tokenizer(&settings, this);
        std::istringstream istr(code);
        ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line);

        for (const Token* tok = tokenizer.tokens(); tok; tok = tok->next()) {
            if (tok->str() == "x" && tok->linenr() == linenr) {
                if (std::any_of(tok->values().begin(), tok->values().end(), [&](const ValueFlow::Value& val) {
                    if (!val.isSymbolicValue())
                        return false;
                    if (val.isKnown() && val.intvalue == value && val.tokvalue->expressionString() == expr)
                        return true;
                    return false;
                }))
                    return true;
            }
        }

        return false;
    }

#define testValueOfXImpossible(...) testValueOfXImpossible_(__FILE__, __LINE__, __VA_ARGS__)
    bool testValueOfXImpossible_(const char* file, int line, const char code[], unsigned int linenr, int value) {
        // Tokenize..
        Tokenizer tokenizer(&settings, this);
        std::istringstream istr(code);
        ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line);

        for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) {
            if (tok->str() == "x" && tok->linenr() == linenr) {
                if (std::any_of(tok->values().begin(), tok->values().end(), [&](const ValueFlow::Value& val) {
                    if (val.isSymbolicValue())
                        return false;
                    if (val.isImpossible() && val.intvalue == value)
                        return true;
                    return false;
                }))
                    return true;
            }
        }

        return false;
    }

    bool testValueOfXImpossible_(const char* file, int line, const char code[], unsigned int linenr, const std::string& expr, int value)
    {
        // Tokenize..
        Tokenizer tokenizer(&settings, this);
        std::istringstream istr(code);
        ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line);

        for (const Token* tok = tokenizer.tokens(); tok; tok = tok->next()) {
            if (tok->str() == "x" && tok->linenr() == linenr) {
                if (std::any_of(tok->values().begin(), tok->values().end(), [&](const ValueFlow::Value& val) {
                    if (!val.isSymbolicValue())
                        return false;
                    if (val.isImpossible() && val.intvalue == value && val.tokvalue->expressionString() == expr)
                        return true;
                    return false;
                }))
                    return true;
            }
        }

        return false;
    }

#define testValueOfXInconclusive(code, linenr, value) testValueOfXInconclusive_(code, linenr, value, __FILE__, __LINE__)
    bool testValueOfXInconclusive_(const char code[], unsigned int linenr, int value, const char* file, int line) {
        // Tokenize..
        Tokenizer tokenizer(&settings, this);
        std::istringstream istr(code);
        ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line);

        for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) {
            if (tok->str() == "x" && tok->linenr() == linenr) {
                if (std::any_of(tok->values().begin(), tok->values().end(), [&](const ValueFlow::Value& val) {
                    if (val.isSymbolicValue())
                        return false;
                    if (val.isInconclusive() && val.intvalue == value)
                        return true;
                    return false;
                }))
                    return true;
            }
        }

        return false;
    }

#define testValueOfX(...) testValueOfX_(__FILE__, __LINE__, __VA_ARGS__)
    bool testValueOfX_(const char* file, int line, const char code[], unsigned int linenr, int value, const Settings *s = nullptr) {
        const Settings *settings1 = s ? s : &settings;

        // Tokenize..
        Tokenizer tokenizer(settings1, this);
        std::istringstream istr(code);
        ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line);

        for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) {
            if (tok->str() == "x" && tok->linenr() == linenr) {
                if (std::any_of(tok->values().cbegin(), tok->values().cend(), [&](const ValueFlow::Value& v) {
                    return v.isIntValue() && !v.isImpossible() && v.intvalue == value;
                }))
                    return true;
            }
        }

        return false;
    }

    bool testValueOfX_(const char* file, int line, const char code[], unsigned int linenr, const std::string& expr, int value)
    {
        // Tokenize..
        Tokenizer tokenizer(&settings, this);
        std::istringstream istr(code);
        ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line);

        for (const Token* tok = tokenizer.tokens(); tok; tok = tok->next()) {
            if (tok->str() == "x" && tok->linenr() == linenr) {
                if (std::any_of(tok->values().cbegin(), tok->values().cend(), [&](const ValueFlow::Value& v) {
                    return v.isSymbolicValue() && !v.isImpossible() && v.intvalue == value && v.tokvalue->expressionString() == expr;
                }))
                    return true;
            }
        }

        return false;
    }

    bool testValueOfX_(const char* file, int line, const char code[], unsigned int linenr, double value, double diff) {
        // Tokenize..
        Tokenizer tokenizer(&settings, this);
        std::istringstream istr(code);
        ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line);

        for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) {
            if (tok->str() == "x" && tok->linenr() == linenr) {
                if (std::any_of(tok->values().cbegin(), tok->values().cend(), [&](const ValueFlow::Value& v) {
                    return v.isFloatValue() && !v.isImpossible() && v.floatValue >= value - diff && v.floatValue next()) {
            if (tok->str() != "x" || tok->linenr() != linenr)
                continue;

            std::ostringstream ostr;
            for (const ValueFlow::Value &v : tok->values()) {
                for (const ValueFlow::Value::ErrorPathItem &ep : v.errorPath) {
                    const Token *eptok = ep.first;
                    const std::string &msg = ep.second;
                    ostr linenr() linenr() == linenr) {
                if (std::any_of(tok->values().cbegin(), tok->values().cend(), [&](const ValueFlow::Value& v) {
                    return v.valueType == type && Token::simpleMatch(v.tokvalue, value, len);
                }))
                    return true;
            }
        }

        return false;
    }

#define testLifetimeOfX(...) testLifetimeOfX_(__FILE__, __LINE__, __VA_ARGS__)
    bool testLifetimeOfX_(const char* file, int line, const char code[], unsigned int linenr, const char value[], ValueFlow::Value::LifetimeScope lifetimeScope = ValueFlow::Value::LifetimeScope::Local) {
        // Tokenize..
        Tokenizer tokenizer(&settings, this);
        std::istringstream istr(code);
        ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line);

        const std::size_t len = strlen(value);
        for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) {
            if (tok->str() == "x" && tok->linenr() == linenr) {
                if (std::any_of(tok->values().cbegin(), tok->values().cend(), [&](const ValueFlow::Value& v) {
                    return v.isLifetimeValue() && v.lifetimeScope == lifetimeScope && Token::simpleMatch(v.tokvalue, value, len);
                }))
                    return true;
            }
        }

        return false;
    }

    bool testValueOfX_(const char* file, int line, const char code[], unsigned int linenr, int value, ValueFlow::Value::ValueType type) {
        // Tokenize..
        Tokenizer tokenizer(&settings, this);
        std::istringstream istr(code);
        ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line);

        for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) {
            if (tok->str() == "x" && tok->linenr() == linenr) {
                if (std::any_of(tok->values().cbegin(), tok->values().cend(), [&](const ValueFlow::Value& v) {
                    return v.valueType == type && v.intvalue == value;
                }))
                    return true;
            }
        }

        return false;
    }

    bool testValueOfX_(const char* file, int line, const char code[], unsigned int linenr, ValueFlow::Value::MoveKind moveKind) {
        // Tokenize..
        Tokenizer tokenizer(&settings, this);
        std::istringstream istr(code);
        ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line);

        for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) {
            if (tok->str() == "x" && tok->linenr() == linenr) {
                if (std::any_of(tok->values().cbegin(), tok->values().cend(), [&](const ValueFlow::Value& v) {
                    return v.isMovedValue() && v.moveKind == moveKind;
                }))
                    return true;
            }
        }

        return false;
    }

#define testConditionalValueOfX(code, linenr, value) testConditionalValueOfX_(code, linenr, value, __FILE__, __LINE__)
    bool testConditionalValueOfX_(const char code[], unsigned int linenr, int value, const char* file, int line) {
        // Tokenize..
        Tokenizer tokenizer(&settings, this);
        std::istringstream istr(code);
        ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line);

        for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) {
            if (tok->str() == "x" && tok->linenr() == linenr) {
                if (std::any_of(tok->values().cbegin(), tok->values().cend(), [&](const ValueFlow::Value& v) {
                    return v.isIntValue() && v.intvalue == value && v.condition;
                }))
                    return true;
            }
        }

        return false;
    }

#define bailout(...) bailout_(__FILE__, __LINE__, __VA_ARGS__)
    void bailout_(const char* file, int line, const char code[]) {
        const Settings s = settingsBuilder().debugwarnings().build();
        errout.str("");

        std::vector files(1, "test.cpp");
        Tokenizer tokenizer(&s, this);
        PreprocessorHelper::preprocess(code, files, tokenizer);

        // Tokenize..
        ASSERT_LOC(tokenizer.simplifyTokens1(""), file, line);
    }

#define tokenValues(...) tokenValues_(__FILE__, __LINE__, __VA_ARGS__)
    std::list tokenValues_(const char* file, int line, const char code[], const char tokstr[], const Settings *s = nullptr) {
        Tokenizer tokenizer(s ? s : &settings, this);
        std::istringstream istr(code);
        errout.str("");
        ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line);
        const Token *tok = Token::findmatch(tokenizer.tokens(), tokstr);
        return tok ? tok->values() : std::list();
    }

    std::list tokenValues_(const char* file, int line, const char code[], const char tokstr[], ValueFlow::Value::ValueType vt, const Settings *s = nullptr) {
        std::list values = tokenValues_(file, line, code, tokstr, s);
        values.remove_if([&](const ValueFlow::Value& v) {
            return v.valueType != vt;
        });
        return values;
    }

#define lifetimeValues(...) lifetimeValues_(__FILE__, __LINE__, __VA_ARGS__)
    std::vector lifetimeValues_(const char* file, int line, const char code[], const char tokstr[], const Settings *s = nullptr) {
        std::vector result;
        Tokenizer tokenizer(s ? s : &settings, this);
        std::istringstream istr(code);
        errout.str("");
        ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line);
        const Token *tok = Token::findmatch(tokenizer.tokens(), tokstr);
        if (!tok)
            return result;
        for (const ValueFlow::Value& value:tok->values()) {
            if (!value.isLifetimeValue())
                continue;
            if (!value.tokvalue)
                continue;
            result.push_back(value.tokvalue->expressionString());
        }
        return result;
    }

#define valueOfTok(code, tokstr) valueOfTok_(code, tokstr, __FILE__, __LINE__)
    ValueFlow::Value valueOfTok_(const char code[], const char tokstr[], const char* file, int line) {
        std::list values = removeImpossible(tokenValues_(file, line, code, tokstr));
        return values.size() == 1U && !values.front().isTokValue() ? values.front() : ValueFlow::Value();
    }

    static std::list removeSymbolicTok(std::list values)
    {
        values.remove_if([](const ValueFlow::Value& v) {
            return v.isSymbolicValue() || v.isTokValue();
        });
        return values;
    }

    static std::list removeImpossible(std::list values)
    {
        values.remove_if(std::mem_fn(&ValueFlow::Value::isImpossible));
        return values;
    }

    void valueFlowNumber() {
        ASSERT_EQUALS(123, valueOfTok("x=123;", "123").intvalue);
        ASSERT_EQUALS_DOUBLE(192.0, valueOfTok("x=0x0.3p10;", "0x0.3p10").floatValue, 1e-5); // 3 * 16^-1 * 2^10 = 192
        ASSERT(std::fabs(valueOfTok("x=0.5;", "0.5").floatValue - 0.5) < 0.1);
        ASSERT_EQUALS(10, valueOfTok("enum {A=10,B=15}; x=A+0;", "+").intvalue);
        ASSERT_EQUALS(0, valueOfTok("x=false;", "false").intvalue);
        ASSERT_EQUALS(1, valueOfTok("x=true;", "true").intvalue);
        ASSERT_EQUALS(0, valueOfTok("x(NULL);", "NULL").intvalue);
        ASSERT_EQUALS((int)('a'), valueOfTok("x='a';", "'a'").intvalue);
        ASSERT_EQUALS((int)('\n'), valueOfTok("x='\\n';", "'\\n'").intvalue);
        TODO_ASSERT_EQUALS(0xFFFFFFFF00000000, 0, valueOfTok("x=0xFFFFFFFF00000000;", "0xFFFFFFFF00000000").intvalue); // #7701
        ASSERT_EQUALS_DOUBLE(16, valueOfTok("x=(double)16;", "(").floatValue, 1e-5);
        ASSERT_EQUALS_DOUBLE(0.0625, valueOfTok("x=1/(double)16;", "/").floatValue, 1e-5);

        // scope
        {
            const char code[] = "namespace N { enum E {e0,e1}; }\n"
                                "void foo() { x = N::e1; }";
            ASSERT_EQUALS(1, valueOfTok(code, "::").intvalue);
        }
    }

    void valueFlowString() {
        const char *code;

        // valueFlowAfterAssign
        code  = "const char * f() {\n"
                "    static const char *x;\n"
                "    if (a) x = \"123\";\n"
                "    return x;\n"
                "}";
        ASSERT_EQUALS(true, testValueOfX(code, 4, "\"123\"", ValueFlow::Value::ValueType::TOK));

        // valueFlowSubFunction
        code  = "void dostuff(const char *x) {\n"
                "  f(x);\n"
                "}\n"
                "\n"
                "void test() { dostuff(\"abc\"); }";
        ASSERT_EQUALS(true, testValueOfX(code, 2, "\"abc\"", ValueFlow::Value::ValueType::TOK));
    }

    void valueFlowPointerAlias() {
        const char *code;
        std::list values;

        code  = "const char * f() {\n"
                "    static const char *x;\n"
                "    static char ret[10];\n"
                "    if (a) x = &ret[0];\n"
                "    return x;\n"
                "}";
        ASSERT_EQUALS(true, testValueOfX(code, 5, "& ret [ 0 ]", ValueFlow::Value::ValueType::TOK));

        // dead pointer
        code  = "void f() {\n"
                "  int *x;\n"
                "  if (cond) { int i; x = &i; }\n"
                "  *x = 0;\n"  //  don't consider > condition
               "}";
        ASSERT_EQUALS(false, testValueOfX(code, 2U, 1));

        code = "void f(int x) {\n" // not unsigned => don't consider > condition
               "    int a = x;\n"
               "    if (x > 0) {}\n"
               "}";
        ASSERT_EQUALS(false, testValueOfX(code, 2U, 0));

        code = "void f(int *x) {\n"
               "    *x = 100;\n"
               "    if (x) {}\n"
               "}";
        ASSERT_EQUALS(true, testValueOfX(code, 2U, 0));

        code = "extern const int x;\n"
               "void f() {\n"
               "    int a = x;\n"
               "    if (x == 123) {}\n"
               "}";
        ASSERT_EQUALS(true, testValueOfX(code, 3U, 123));

        // after loop
        code = "void f(struct X *x) {\n"
               "  do {\n"
               "    if (!x)\n"
               "      break;\n"
               "  } while (x->a);\n"
               "  if (x) {}\n"
               "}\n";
        ASSERT_EQUALS(false, testValueOfX(code, 5U, 0));
        ASSERT_EQUALS(false, testValueOfXKnown(code, 3U, 1));
        TODO_ASSERT_EQUALS(true, false, testValueOfX(code, 6U, 0));
    }

    void valueFlowBeforeConditionAssignIncDec() {  // assignment / increment
        const char *code;

        code = "void f(int x) {\n"
               "   x = 2 + x;\n"
               "   if (x == 65);\n"
               "}";
        ASSERT_EQUALS(false, testValueOfX(code, 2U, 65));

        code = "void f(int x) {\n"
               "   x = y = 2 + x;\n"
               "   if (x == 65);\n"
               "}";
        ASSERT_EQUALS(false, testValueOfX(code, 2U, 65));

        code = "void f(int x) {\n"
               "   a[x++] = 0;\n"
               "   if (x == 5);\n"
               "}";
        ASSERT_EQUALS(false, testValueOfX(code, 2U, 5));

        code = "void f(int x) {\n"
               "   a = x;\n"
               "   x++;\n"
               "   if (x == 4);\n"
               "}";
        ASSERT_EQUALS(true, testValueOfX(code, 2U, 3));

        // compound assignment += , -= , ...
        code = "void f(int x) {\n"
               "   a = x;\n"
               "   x += 2;\n"
               "   if (x == 4);\n"
               "}";
        ASSERT_EQUALS(true, testValueOfX(code, 2U, 2));
        ASSERT_EQUALS(true, testValueOfX(code, 3U, 2));

        code = "void f(int x) {\n"
               "   a = x;\n"
               "   x -= 2;\n"
               "   if (x == 4);\n"
               "}";
        ASSERT_EQUALS(true, testValueOfX(code, 2U, 6));
        ASSERT_EQUALS(true, testValueOfX(code, 3U, 6));

        code = "void f(int x) {\n"
               "   a = x;\n"
               "   x *= 2;\n"
               "   if (x == 42);\n"
               "}";
        ASSERT_EQUALS(true, testValueOfX(code, 2U, 21));
        ASSERT_EQUALS(true, testValueOfX(code, 3U, 21));

        code = "void f(int x) {\n"
               "   a = x;\n"
               "   x /= 5;\n"
               "   if (x == 42);\n"
               "}";
        ASSERT_EQUALS(true, testValueOfX(code, 2U, 210));
        ASSERT_EQUALS(true, testValueOfX(code, 3U, 210));

        // bailout: assignment
        bailout("void f(int x) {\n"
                "    x = y;\n"
                "    if (x == 123) {}\n"
                "}");
        ASSERT_EQUALS(
            "[test.cpp:2]: (debug) valueFlowConditionExpressions bailout: Skipping function due to incomplete variable y\n",
            errout.str());
    }

    void valueFlowBeforeConditionAndAndOrOrGuard() { // guarding by &&
        const char *code;

        code = "void f(int x) {\n"
               "    if (!x || \n"  //  don't use condition
               "    a = x;\n"
               "    for (i=1;ia : 0) {}\n"
               "}";
        ASSERT_EQUALS(true, testValueOfX(code, 2U, 0));

        code = "void f(int x, int y) {\n"
               "    a = x;\n"
               "    if (y){}\n"
               "    if (x==123){}\n"
               "}";
        ASSERT_EQUALS(true, testValueOfX(code, 2U, 123));
    }

    void valueFlowBeforeConditionSizeof() { // skip sizeof
        const char *code;

        code = "void f(int *x) {\n"
               "    sizeof(x[0]);\n"
               "    if (x==63){}\n"
               "}";
        ASSERT_EQUALS(false, testValueOfX(code, 2U, 63));

        code = "void f(int *x) {\n"
               "    char a[sizeof x.y];\n"
               "    if (x==0){}\n"
               "}";
        ASSERT_EQUALS(false, testValueOfX(code, 2U, 0));
    }

    void valueFlowBeforeConditionIfElse() { // bailout: if/else/etc
        const char *code;

        code = "void f(X * x) {\n"
               "  a = x;\n"
               "  if ((x != NULL) &&\n"
               "      (a(x->name, html)) &&\n"
               "      (a(x->name, body))) {}\n"
               "  if (x != NULL) { }\n"
               "}";
        ASSERT_EQUALS(true, testValueOfX(code, 2U, 0));
        ASSERT_EQUALS(true, testValueOfX(code, 3U, 0));
        ASSERT_EQUALS(false, testValueOfX(code, 4U, 0));
        ASSERT_EQUALS(false, testValueOfX(code, 5U, 0));

        bailout("void f(int x) {\n"
                "    if (x != 123) { b = x; }\n"
                "    if (x == 123) {}\n"
                "}");
        ASSERT_EQUALS(
            "[test.cpp:2]: (debug) valueFlowConditionExpressions bailout: Skipping function due to incomplete variable b\n",
            errout.str());

        code = "void f(int x, bool abc) {\n"
               "  a = x;\n"
               "  if (abc) { x = 1; }\n" // 

Web Proxy Viewer  |  New URL  |  Original Page