示例#1
0
static void valueFlowBeforeCondition(TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings)
{
    for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
        unsigned int varid;
        MathLib::bigint num;
        const Variable *var;
        if (tok->isComparisonOp() && tok->astOperand1() && tok->astOperand2()) {
            if (tok->astOperand1()->isName() && tok->astOperand2()->isNumber()) {
                varid = tok->astOperand1()->varId();
                var = tok->astOperand1()->variable();
                num = MathLib::toLongNumber(tok->astOperand2()->str());
            } else if (tok->astOperand1()->isNumber() && tok->astOperand2()->isName()) {
                varid = tok->astOperand2()->varId();
                var = tok->astOperand2()->variable();
                num = MathLib::toLongNumber(tok->astOperand1()->str());
            } else {
                continue;
            }
        } else if (Token::Match(tok->previous(), "if|while ( %var% %oror%|&&|)") ||
                   Token::Match(tok, "%oror%|&& %var% %oror%|&&|)")) {
            varid = tok->next()->varId();
            var = tok->next()->variable();
            num = 0;
        } else if (tok->str() == "!" && tok->astOperand1() && tok->astOperand1()->isName()) {
            varid = tok->astOperand1()->varId();
            var = tok->astOperand1()->variable();
            num = 0;
        } else {
            continue;
        }

        if (varid == 0U || !var)
            continue;

        // bailout: global non-const variables
        if (var->isGlobal() && !var->isConst()) {
            if (settings->debugwarnings)
                bailout(tokenlist, errorLogger, tok, "global variable " + var->nameToken()->str());
            continue;
        }

        // bailout: while-condition, variable is changed in while loop
        for (const Token *tok2 = tok; tok2; tok2 = tok2->astParent()) {
            if (tok2->astParent() || tok2->str() != "(" || !Token::simpleMatch(tok2->link(), ") {"))
                continue;

            if (Token::Match(tok2->previous(), "for|while (")) {
                const Token *start = tok2->link()->next();
                const Token *end   = start->link();
                if (Token::findmatch(start,"++|--| %varid% ++|--|=",end,varid)) {
                    varid = 0U;
                    if (settings->debugwarnings)
                        bailout(tokenlist, errorLogger, tok, "variable " + var->nameToken()->str() + " used in loop");
                }
            }

            // if,macro => bailout
            else if (Token::simpleMatch(tok2->previous(), "if (") && tok2->previous()->isExpandedMacro()) {
                varid = 0U;
                if (settings->debugwarnings)
                    bailout(tokenlist, errorLogger, tok, "variable " + var->nameToken()->str() + ", condition is defined in macro");
            }
        }
        if (varid == 0U)
            continue;

        // extra logic for unsigned variables 'i>=1' => possible value can also be 0
        const ValueFlow::Value val(tok, num);
        ValueFlow::Value val2;
        if (num==1U && Token::Match(tok,"<=|>=")) {
            bool isunsigned = false;
            for (const Token* type = var->typeStartToken(); type && type->varId() == 0U; type = type->next())
                isunsigned |= type->isUnsigned();
            if (isunsigned)
                val2 = ValueFlow::Value(tok,0);
        }

        bool inconclusive = false;
        for (Token *tok2 = tok->previous(); ; tok2 = tok2->previous()) {
            if (!tok2) {
                if (settings->debugwarnings) {
                    std::list<ErrorLogger::ErrorMessage::FileLocation> callstack;
                    callstack.push_back(ErrorLogger::ErrorMessage::FileLocation(tok,tokenlist));
                    ErrorLogger::ErrorMessage errmsg(callstack, Severity::debug, "iterated too far", "debugValueFlowBeforeCondition", false);
                    errorLogger->reportErr(errmsg);
                }
                break;
            }

            if (tok2->varId() == varid) {
                // bailout: assignment
                if (Token::Match(tok2->previous(), "!!* %var% =")) {
                    if (settings->debugwarnings)
                        bailout(tokenlist, errorLogger, tok2, "assignment of " + tok2->str());
                    break;
                }

                // bailout: variable is used in rhs in assignment to itself
                if (bailoutSelfAssignment(tok2)) {
                    if (settings->debugwarnings)
                        bailout(tokenlist, errorLogger, tok2, "variable " + tok2->str() + " is used in rhs in assignment to itself");
                    break;
                }

                // assigned by subfunction?
                bool inconclusive2 = false;
                if (bailoutFunctionPar(tok2,val2.condition ? val2 : val, settings, &inconclusive2)) {
                    if (settings->debugwarnings)
                        bailout(tokenlist, errorLogger, tok2, "possible assignment of " + tok2->str() + " by subfunction");
                    break;
                }
                inconclusive |= inconclusive2;
                val2.inconclusive |= inconclusive2;

                // skip if variable is conditionally used in ?: expression
                if (const Token *parent = skipValueInConditionalExpression(tok2)) {
                    if (settings->debugwarnings)
                        bailout(tokenlist,
                                errorLogger,
                                tok2,
                                "no simplification of " + tok2->str() + " within " + (Token::Match(parent,"[?:]") ? "?:" : parent->str()) + " expression");
                    continue;
                }

                tok2->values.push_back(val);
                tok2->values.back().inconclusive = inconclusive;
                if (val2.condition)
                    tok2->values.push_back(val2);
                if (var && tok2 == var->nameToken())
                    break;
            }

            // skip sizeof..
            if (tok2->str() == ")" && Token::Match(tok2->link()->previous(), "typeof|sizeof ("))
                tok2 = tok2->link();

            if (tok2->str() == "}") {
                if (Token::findmatch(tok2->link(), "%varid%", tok2, varid)) {
                    if (settings->debugwarnings)
                        bailout(tokenlist, errorLogger, tok2, "variable " + var->nameToken()->str() + " stopping on }");
                    break;
                } else {
                    tok2 = tok2->link();
                }
            } else if (tok2->str() == "{") {
                // if variable is assigned in loop don't look before the loop
                if (tok2->previous() &&
                    (Token::Match(tok2->previous(), "do") ||
                     (tok2->str() == ")" && Token::Match(tok2->linkAt(-1)->previous(), "for|while (")))) {

                    const Token *start = tok2;
                    const Token *end   = start->link();
                    if (Token::findmatch(start,"++|--| %varid% ++|--|=",end,varid)) {
                        if (settings->debugwarnings)
                            bailout(tokenlist, errorLogger, tok2, "variable " + var->nameToken()->str() + " is assigned in loop. so valueflow analysis bailout when start of loop is reached.");
                        break;
                    }
                }

                // Global variable : stop when leaving the function scope
                if (!var->isLocal()) {
                    if (!Token::Match(tok2->previous(), ")|else|do {"))
                        break;
                    if (Token::Match(tok2->previous(), ") {") &&
                        !Token::Match(tok2->linkAt(-1)->previous(), "if|for|while ("))
                        break;
                }
            } else if (tok2->str() == ";") {
                const Token *parent = tok2->previous();
                while (parent && !Token::Match(parent, "return|break|continue|goto"))
                    parent = parent->astParent();
                // reaching a break/continue/return
                if (parent) {
                    if (settings->debugwarnings)
                        bailout(tokenlist, errorLogger, tok2, "variable " + var->nameToken()->str() + " stopping on " + parent->str());
                    break;
                }
            }

            // goto label
            if (Token::Match(tok2, "[;{}] %var% :")) {
                if (settings->debugwarnings)
                    bailout(tokenlist, errorLogger, tok2, "variable " + var->nameToken()->str() + " stopping on goto label");
                break;
            }
        }
    }
}
示例#2
0
static bool valueFlowForward(Token * const               startToken,
                             const Token * const         endToken,
                             const Variable * const      var,
                             const unsigned int          varid,
                             std::list<ValueFlow::Value> values,
                             const bool                  constValue,
                             TokenList * const           tokenlist,
                             ErrorLogger * const         errorLogger,
                             const Settings * const      settings)
{
    int indentlevel = 0;
    unsigned int number_of_if = 0;
    int varusagelevel = -1;
    bool returnStatement = false;  // current statement is a return, stop analysis at the ";"

    for (Token *tok2 = startToken; tok2 && tok2 != endToken; tok2 = tok2->next()) {
        if (indentlevel >= 0 && tok2->str() == "{")
            ++indentlevel;
        else if (indentlevel >= 0 && tok2->str() == "}")
            --indentlevel;

        if (Token::Match(tok2, "sizeof|typeof|typeid ("))
            tok2 = tok2->linkAt(1);

        // conditional block of code that assigns variable..
        else if (Token::Match(tok2, "%var% (") && Token::simpleMatch(tok2->linkAt(1), ") {")) {
            // Should scope be skipped because variable value is checked?
            bool skip = false;
            for (std::list<ValueFlow::Value>::iterator it = values.begin(); it != values.end(); ++it) {
                if (conditionIsFalse(tok2->next()->astOperand2(), varid, *it)) {
                    skip = true;
                    break;
                }
            }
            if (skip) {
                // goto '{'
                tok2 = tok2->linkAt(1)->next();
                // goto '}'
                tok2 = tok2->link();
                continue;
            }

            Token * const start = tok2->linkAt(1)->next();
            Token * const end   = start->link();
            bool varusage = (indentlevel >= 0 && constValue && number_of_if == 0U) ?
                            isVariableChanged(start,end,varid) :
                            (nullptr != Token::findmatch(start, "%varid%", end, varid));
            if (varusage) {
                varusagelevel = indentlevel;

                // TODO: don't check noreturn scopes
                if (number_of_if > 0U || Token::findmatch(tok2, "%varid%", start, varid)) {
                    if (settings->debugwarnings)
                        bailout(tokenlist, errorLogger, tok2, "variable " + var->nameToken()->str() + " is assigned in conditional code");
                    return false;
                }

                if (var->isStatic()) {
                    if (settings->debugwarnings)
                        bailout(tokenlist, errorLogger, tok2, "variable " + var->nameToken()->str() + " bailout when conditional code that contains var is seen");
                    return false;
                }

                // Remove conditional values
                std::list<ValueFlow::Value>::iterator it;
                for (it = values.begin(); it != values.end();) {
                    if (it->condition || it->conditional)
                        values.erase(it++);
                    else
                        ++it;
                }
            }

            // noreturn scopes..
            if ((number_of_if > 0 || Token::findmatch(tok2, "%varid%", start, varid)) &&
                (Token::findmatch(start, "return|continue|break|throw", end) ||
                 (Token::simpleMatch(end,"} else {") && Token::findmatch(end, "return|continue|break|throw", end->linkAt(2))))) {
                if (settings->debugwarnings)
                    bailout(tokenlist, errorLogger, tok2, "variable " + var->nameToken()->str() + ". noreturn conditional scope.");
                return false;
            }

            if (isVariableChanged(start, end, varid)) {
                if (number_of_if == 0 &&
                    Token::simpleMatch(tok2, "if (") &&
                    !(Token::simpleMatch(end, "} else {") &&
                      (Token::findmatch(end, "%varid%", end->linkAt(2), varid) ||
                       Token::findmatch(end, "return|continue|break|throw", end->linkAt(2))))) {
                    ++number_of_if;
                    tok2 = end;
                } else {
                    if (settings->debugwarnings)
                        bailout(tokenlist, errorLogger, tok2, "variable " + var->nameToken()->str() + " is assigned in conditional code");
                    return false;
                }
            }
        }

        else if (tok2->str() == "}" && indentlevel == varusagelevel) {
            ++number_of_if;

            // Set "conditional" flag for all values
            std::list<ValueFlow::Value>::iterator it;
            for (it = values.begin(); it != values.end(); ++it)
                it->conditional = true;

            if (Token::simpleMatch(tok2,"} else {"))
                tok2 = tok2->linkAt(2);
        }

        else if (indentlevel <= 0 && Token::Match(tok2, "break|continue|goto")) {
            if (settings->debugwarnings)
                bailout(tokenlist, errorLogger, tok2, "variable " + var->nameToken()->str() + ". noreturn conditional scope.");
            return false;
        }

        else if (indentlevel <= 0 && Token::Match(tok2, "return|throw"))
            returnStatement = true;

        else if (returnStatement && tok2->str() == ";")
            return false;

        if (tok2->varId() == varid) {
            // bailout: assignment
            if (Token::Match(tok2->previous(), "!!* %var% %op%") && tok2->next()->isAssignmentOp()) {
                // simplify rhs
                for (Token *tok3 = tok2->tokAt(2); tok3; tok3 = tok3->next()) {
                    if (tok3->varId() == varid) {
                        std::list<ValueFlow::Value>::const_iterator it;
                        for (it = values.begin(); it != values.end(); ++it)
                            setTokenValue(tok2, *it);
                    } else if (Token::Match(tok3, "++|--|?|:|;"))
                        break;
                }
                if (settings->debugwarnings)
                    bailout(tokenlist, errorLogger, tok2, "assignment of " + tok2->str());
                return false;
            }

            // bailout increment/decrement for now..
            if (Token::Match(tok2->previous(), "++|-- %var%") || Token::Match(tok2, "%var% ++|--")) {
                if (settings->debugwarnings)
                    bailout(tokenlist, errorLogger, tok2, "increment/decrement of " + tok2->str());
                return false;
            }

            // bailout: possible assignment using >>
            if (Token::Match(tok2->previous(), ">> %var% >>|;")) {
                const Token *parent = tok2->previous();
                while (Token::simpleMatch(parent,">>"))
                    parent = parent->astParent();
                if (!parent) {
                    if (settings->debugwarnings)
                        bailout(tokenlist, errorLogger, tok2, "Possible assignment of " + tok2->str() + " using >>");
                    return false;
                }
            }

            // skip if variable is conditionally used in ?: expression
            if (const Token *parent = skipValueInConditionalExpression(tok2)) {
                if (settings->debugwarnings)
                    bailout(tokenlist,
                            errorLogger,
                            tok2,
                            "no simplification of " + tok2->str() + " within " + (Token::Match(parent,"[?:]") ? "?:" : parent->str()) + " expression");
                continue;
            }

            {
                std::list<ValueFlow::Value>::const_iterator it;
                for (it = values.begin(); it != values.end(); ++it)
                    setTokenValue(tok2, *it);
            }

            // assigned by subfunction?
            bool inconclusive = false;
            if (bailoutFunctionPar(tok2, ValueFlow::Value(), settings, &inconclusive)) {
                if (settings->debugwarnings)
                    bailout(tokenlist, errorLogger, tok2, "possible assignment of " + tok2->str() + " by subfunction");
                return false;
            }
            if (inconclusive) {
                std::list<ValueFlow::Value>::iterator it;
                for (it = values.begin(); it != values.end(); ++it)
                    it->inconclusive = true;
            }
        }
    }
    return true;
}