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; } } } }
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; }