Refactorization: Use SymbolDatabase scope information in ValueFlow
This commit is contained in:
parent
54b6b8e571
commit
a4cc4c3e3f
|
@ -1638,7 +1638,7 @@ bool Tokenizer::tokenize(std::istream &code,
|
||||||
}
|
}
|
||||||
|
|
||||||
list.createAst();
|
list.createAst();
|
||||||
ValueFlow::setValues(&list, _errorLogger, _settings);
|
ValueFlow::setValues(&list, _symbolDatabase, _errorLogger, _settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -3854,7 +3854,7 @@ bool Tokenizer::simplifyTokenList2()
|
||||||
|
|
||||||
list.createAst();
|
list.createAst();
|
||||||
|
|
||||||
ValueFlow::setValues(&list, _errorLogger, _settings);
|
ValueFlow::setValues(&list, _symbolDatabase, _errorLogger, _settings);
|
||||||
|
|
||||||
if (_settings->terminated())
|
if (_settings->terminated())
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -421,231 +421,235 @@ static void valueFlowBitAnd(TokenList *tokenlist)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void valueFlowBeforeCondition(TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings)
|
static void valueFlowBeforeCondition(TokenList *tokenlist, SymbolDatabase *symboldatabase, ErrorLogger *errorLogger, const Settings *settings)
|
||||||
{
|
{
|
||||||
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
|
const std::size_t functions = symboldatabase->functionScopes.size();
|
||||||
unsigned int varid=0;
|
for (std::size_t i = 0; i < functions; ++i) {
|
||||||
MathLib::bigint num=0;
|
const Scope * scope = symboldatabase->functionScopes[i];
|
||||||
const Variable *var=0;
|
for (Token* tok = const_cast<Token*>(scope->classStart); tok != scope->classEnd; tok = tok->next()) {
|
||||||
if (tok->isComparisonOp() && tok->astOperand1() && tok->astOperand2()) {
|
unsigned int varid=0;
|
||||||
if (tok->astOperand1()->isName() && tok->astOperand2()->isNumber()) {
|
MathLib::bigint num=0;
|
||||||
|
const Variable *var=0;
|
||||||
|
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 ( %name% %oror%|&&|)") ||
|
||||||
|
Token::Match(tok, "%oror%|&& %name% %oror%|&&|)")) {
|
||||||
|
varid = tok->next()->varId();
|
||||||
|
var = tok->next()->variable();
|
||||||
|
num = 0;
|
||||||
|
} else if (tok->str() == "!" && tok->astOperand1() && tok->astOperand1()->isName()) {
|
||||||
varid = tok->astOperand1()->varId();
|
varid = tok->astOperand1()->varId();
|
||||||
var = tok->astOperand1()->variable();
|
var = tok->astOperand1()->variable();
|
||||||
num = MathLib::toLongNumber(tok->astOperand2()->str());
|
num = 0;
|
||||||
} else if (tok->astOperand1()->isNumber() && tok->astOperand2()->isName()) {
|
|
||||||
varid = tok->astOperand2()->varId();
|
|
||||||
var = tok->astOperand2()->variable();
|
|
||||||
num = MathLib::toLongNumber(tok->astOperand1()->str());
|
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} else if (Token::Match(tok->previous(), "if|while ( %name% %oror%|&&|)") ||
|
|
||||||
Token::Match(tok, "%oror%|&& %name% %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)
|
if (varid == 0U || !var)
|
||||||
continue;
|
|
||||||
|
|
||||||
// bailout: global non-const variables
|
|
||||||
if (!(var->isLocal() || var->isArgument()) && !var->isConst()) {
|
|
||||||
if (settings->debugwarnings)
|
|
||||||
bailout(tokenlist, errorLogger, tok, "global variable " + var->name());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// bailout: for/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;
|
continue;
|
||||||
|
|
||||||
// Variable changed in 3rd for-expression
|
// bailout: global non-const variables
|
||||||
if (Token::simpleMatch(tok2->previous(), "for (")) {
|
if (!(var->isLocal() || var->isArgument()) && !var->isConst()) {
|
||||||
if (isVariableChanged(tok2->astOperand2()->astOperand2(), tok2->link(), varid)) {
|
|
||||||
varid = 0U;
|
|
||||||
if (settings->debugwarnings)
|
|
||||||
bailout(tokenlist, errorLogger, tok, "variable " + var->name() + " used in loop");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Variable changed in loop code
|
|
||||||
if (Token::Match(tok2->previous(), "for|while (")) {
|
|
||||||
const Token * const start = tok2->link()->next();
|
|
||||||
const Token * const end = start->link();
|
|
||||||
|
|
||||||
if (isVariableChanged(start,end,varid)) {
|
|
||||||
varid = 0U;
|
|
||||||
if (settings->debugwarnings)
|
|
||||||
bailout(tokenlist, errorLogger, tok, "variable " + var->name() + " used in loop");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if,macro => bailout
|
|
||||||
else if (Token::simpleMatch(tok2->previous(), "if (") && tok2->previous()->isExpandedMacro()) {
|
|
||||||
varid = 0U;
|
|
||||||
if (settings->debugwarnings)
|
if (settings->debugwarnings)
|
||||||
bailout(tokenlist, errorLogger, tok, "variable " + var->name() + ", condition is defined in macro");
|
bailout(tokenlist, errorLogger, tok, "global variable " + var->name());
|
||||||
}
|
|
||||||
}
|
|
||||||
if (varid == 0U)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// extra logic for unsigned variables 'i>=1' => possible value can also be 0
|
|
||||||
ValueFlow::Value val(tok, num);
|
|
||||||
val.varId = varid;
|
|
||||||
if (Token::Match(tok, "<|>")) {
|
|
||||||
if (num != 0)
|
|
||||||
continue;
|
continue;
|
||||||
if (!var->typeStartToken()->isUnsigned())
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
ValueFlow::Value val2;
|
|
||||||
if (num==1U && Token::Match(tok,"<=|>=")) {
|
|
||||||
if (var->typeStartToken()->isUnsigned()) {
|
|
||||||
val2 = ValueFlow::Value(tok,0);
|
|
||||||
val2.varId = varid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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: for/while-condition, variable is changed in while loop
|
||||||
// bailout: assignment
|
for (const Token *tok2 = tok; tok2; tok2 = tok2->astParent()) {
|
||||||
if (Token::Match(tok2->previous(), "!!* %name% =")) {
|
if (tok2->astParent() || tok2->str() != "(" || !Token::simpleMatch(tok2->link(), ") {"))
|
||||||
if (settings->debugwarnings)
|
|
||||||
bailout(tokenlist, errorLogger, tok2, "assignment of " + tok2->str());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// increment/decrement
|
|
||||||
if (Token::Match(tok2->previous(), "[;{}] %name% ++|-- ;"))
|
|
||||||
val.intvalue += (tok2->strAt(1)=="++") ? -1 : 1;
|
|
||||||
else if (Token::Match(tok2->tokAt(-2), "[;{}] ++|-- %name% ;"))
|
|
||||||
val.intvalue += (tok2->strAt(-1)=="++") ? -1 : 1;
|
|
||||||
else if (Token::Match(tok2->previous(), "++|-- %name%") || Token::Match(tok2, "%name% ++|--")) {
|
|
||||||
if (settings->debugwarnings)
|
|
||||||
bailout(tokenlist, errorLogger, tok2, "increment/decrement 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Token::Match(tok2->previous(), "sizeof|.")) {
|
|
||||||
const Token *prev = tok2->previous();
|
|
||||||
while (Token::Match(prev,"%name%|.") && prev->str() != "sizeof")
|
|
||||||
prev = prev->previous();
|
|
||||||
if (prev && prev->str() == "sizeof")
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// assigned by subfunction?
|
|
||||||
bool inconclusive = false;
|
|
||||||
if (bailoutFunctionPar(tok2,val2.condition ? val2 : val, settings, &inconclusive)) {
|
|
||||||
if (settings->debugwarnings)
|
|
||||||
bailout(tokenlist, errorLogger, tok2, "possible assignment of " + tok2->str() + " by subfunction");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
val.inconclusive |= inconclusive;
|
|
||||||
val2.inconclusive |= inconclusive;
|
|
||||||
|
|
||||||
// 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;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
setTokenValue(tok2, val);
|
// Variable changed in 3rd for-expression
|
||||||
if (val2.condition)
|
if (Token::simpleMatch(tok2->previous(), "for (")) {
|
||||||
setTokenValue(tok2,val2);
|
if (isVariableChanged(tok2->astOperand2()->astOperand2(), tok2->link(), varid)) {
|
||||||
if (var && tok2 == var->nameToken())
|
varid = 0U;
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// skip sizeof..
|
|
||||||
if (tok2->str() == ")" && Token::Match(tok2->link()->previous(), "typeof|sizeof ("))
|
|
||||||
tok2 = tok2->link();
|
|
||||||
|
|
||||||
// goto label
|
|
||||||
if (Token::Match(tok2, "[;{}] %name% :")) {
|
|
||||||
if (settings->debugwarnings)
|
|
||||||
bailout(tokenlist, errorLogger, tok2->next(), "variable " + var->name() + " stopping on goto label");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tok2->str() == "}") {
|
|
||||||
const Token *vartok = Token::findmatch(tok2->link(), "%varid%", tok2, varid);
|
|
||||||
while (Token::Match(vartok, "%name% = %num% ;") && !vartok->tokAt(2)->getValue(num))
|
|
||||||
vartok = Token::findmatch(vartok->next(), "%varid%", tok2, varid);
|
|
||||||
if (vartok) {
|
|
||||||
if (settings->debugwarnings) {
|
|
||||||
std::string errmsg = "variable ";
|
|
||||||
if (var)
|
|
||||||
errmsg += var->name() + " ";
|
|
||||||
errmsg += "stopping on }";
|
|
||||||
bailout(tokenlist, errorLogger, tok2, errmsg);
|
|
||||||
}
|
|
||||||
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::simpleMatch(tok2->previous(), "do") ||
|
|
||||||
(tok2->strAt(-1) == ")" && Token::Match(tok2->linkAt(-1)->previous(), "for|while (")))) {
|
|
||||||
|
|
||||||
const Token *start = tok2;
|
|
||||||
const Token *end = start->link();
|
|
||||||
if (isVariableChanged(start,end,varid)) {
|
|
||||||
if (settings->debugwarnings)
|
if (settings->debugwarnings)
|
||||||
bailout(tokenlist, errorLogger, tok2, "variable " + var->name() + " is assigned in loop. so valueflow analysis bailout when start of loop is reached.");
|
bailout(tokenlist, errorLogger, tok, "variable " + var->name() + " used in loop");
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Global variable : stop when leaving the function scope
|
// Variable changed in loop code
|
||||||
if (!var->isLocal()) {
|
if (Token::Match(tok2->previous(), "for|while (")) {
|
||||||
if (!Token::Match(tok2->previous(), ")|else|do {"))
|
const Token * const start = tok2->link()->next();
|
||||||
|
const Token * const end = start->link();
|
||||||
|
|
||||||
|
if (isVariableChanged(start,end,varid)) {
|
||||||
|
varid = 0U;
|
||||||
|
if (settings->debugwarnings)
|
||||||
|
bailout(tokenlist, errorLogger, tok, "variable " + var->name() + " 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->name() + ", condition is defined in macro");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (varid == 0U)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// extra logic for unsigned variables 'i>=1' => possible value can also be 0
|
||||||
|
ValueFlow::Value val(tok, num);
|
||||||
|
val.varId = varid;
|
||||||
|
if (Token::Match(tok, "<|>")) {
|
||||||
|
if (num != 0)
|
||||||
|
continue;
|
||||||
|
if (!var->typeStartToken()->isUnsigned())
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ValueFlow::Value val2;
|
||||||
|
if (num==1U && Token::Match(tok,"<=|>=")) {
|
||||||
|
if (var->typeStartToken()->isUnsigned()) {
|
||||||
|
val2 = ValueFlow::Value(tok,0);
|
||||||
|
val2.varId = varid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Token *tok2 = tok->previous(); ; tok2 = tok2->previous()) {
|
||||||
|
if (!tok2 || tok2->next() == scope->classStart) {
|
||||||
|
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(), "!!* %name% =")) {
|
||||||
|
if (settings->debugwarnings)
|
||||||
|
bailout(tokenlist, errorLogger, tok2, "assignment of " + tok2->str());
|
||||||
break;
|
break;
|
||||||
if (Token::simpleMatch(tok2->previous(), ") {") &&
|
}
|
||||||
!Token::Match(tok2->linkAt(-1)->previous(), "if|for|while ("))
|
|
||||||
|
// increment/decrement
|
||||||
|
if (Token::Match(tok2->previous(), "[;{}] %name% ++|-- ;"))
|
||||||
|
val.intvalue += (tok2->strAt(1)=="++") ? -1 : 1;
|
||||||
|
else if (Token::Match(tok2->tokAt(-2), "[;{}] ++|-- %name% ;"))
|
||||||
|
val.intvalue += (tok2->strAt(-1)=="++") ? -1 : 1;
|
||||||
|
else if (Token::Match(tok2->previous(), "++|-- %name%") || Token::Match(tok2, "%name% ++|--")) {
|
||||||
|
if (settings->debugwarnings)
|
||||||
|
bailout(tokenlist, errorLogger, tok2, "increment/decrement 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Token::Match(tok2->previous(), "sizeof|.")) {
|
||||||
|
const Token *prev = tok2->previous();
|
||||||
|
while (Token::Match(prev,"%name%|.") && prev->str() != "sizeof")
|
||||||
|
prev = prev->previous();
|
||||||
|
if (prev && prev->str() == "sizeof")
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// assigned by subfunction?
|
||||||
|
bool inconclusive = false;
|
||||||
|
if (bailoutFunctionPar(tok2,val2.condition ? val2 : val, settings, &inconclusive)) {
|
||||||
|
if (settings->debugwarnings)
|
||||||
|
bailout(tokenlist, errorLogger, tok2, "possible assignment of " + tok2->str() + " by subfunction");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
val.inconclusive |= inconclusive;
|
||||||
|
val2.inconclusive |= inconclusive;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
setTokenValue(tok2, val);
|
||||||
|
if (val2.condition)
|
||||||
|
setTokenValue(tok2,val2);
|
||||||
|
if (var && tok2 == var->nameToken())
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (tok2->str() == ";") {
|
|
||||||
const Token *parent = tok2->previous();
|
// skip sizeof..
|
||||||
while (parent && !Token::Match(parent, "return|break|continue|goto"))
|
if (tok2->str() == ")" && Token::Match(tok2->link()->previous(), "typeof|sizeof ("))
|
||||||
parent = parent->astParent();
|
tok2 = tok2->link();
|
||||||
// reaching a break/continue/return
|
|
||||||
if (parent) {
|
// goto label
|
||||||
|
if (Token::Match(tok2, "[;{}] %name% :")) {
|
||||||
if (settings->debugwarnings)
|
if (settings->debugwarnings)
|
||||||
bailout(tokenlist, errorLogger, tok2, "variable " + var->name() + " stopping on " + parent->str());
|
bailout(tokenlist, errorLogger, tok2->next(), "variable " + var->name() + " stopping on goto label");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tok2->str() == "}") {
|
||||||
|
const Token *vartok = Token::findmatch(tok2->link(), "%varid%", tok2, varid);
|
||||||
|
while (Token::Match(vartok, "%name% = %num% ;") && !vartok->tokAt(2)->getValue(num))
|
||||||
|
vartok = Token::findmatch(vartok->next(), "%varid%", tok2, varid);
|
||||||
|
if (vartok) {
|
||||||
|
if (settings->debugwarnings) {
|
||||||
|
std::string errmsg = "variable ";
|
||||||
|
if (var)
|
||||||
|
errmsg += var->name() + " ";
|
||||||
|
errmsg += "stopping on }";
|
||||||
|
bailout(tokenlist, errorLogger, tok2, errmsg);
|
||||||
|
}
|
||||||
|
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::simpleMatch(tok2->previous(), "do") ||
|
||||||
|
(tok2->strAt(-1) == ")" && Token::Match(tok2->linkAt(-1)->previous(), "for|while (")))) {
|
||||||
|
|
||||||
|
const Token *start = tok2;
|
||||||
|
const Token *end = start->link();
|
||||||
|
if (isVariableChanged(start,end,varid)) {
|
||||||
|
if (settings->debugwarnings)
|
||||||
|
bailout(tokenlist, errorLogger, tok2, "variable " + var->name() + " 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::simpleMatch(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->name() + " stopping on " + parent->str());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1010,179 +1014,185 @@ static bool valueFlowForward(Token * const startToken,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void valueFlowAfterAssign(TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings)
|
static void valueFlowAfterAssign(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings)
|
||||||
{
|
{
|
||||||
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
|
const std::size_t functions = symboldatabase->functionScopes.size();
|
||||||
// Assignment
|
for (std::size_t i = 0; i < functions; ++i) {
|
||||||
if ((tok->str() != "=") || (tok->astParent()))
|
const Scope * scope = symboldatabase->functionScopes[i];
|
||||||
continue;
|
for (Token* tok = const_cast<Token*>(scope->classStart); tok != scope->classEnd; tok = tok->next()) {
|
||||||
|
// Assignment
|
||||||
|
if ((tok->str() != "=") || (tok->astParent()))
|
||||||
|
continue;
|
||||||
|
|
||||||
// Lhs should be a variable
|
// Lhs should be a variable
|
||||||
if (!tok->astOperand1() || !tok->astOperand1()->isName())
|
if (!tok->astOperand1() || !tok->astOperand1()->varId())
|
||||||
continue;
|
continue;
|
||||||
const unsigned int varid = tok->astOperand1()->varId();
|
const unsigned int varid = tok->astOperand1()->varId();
|
||||||
if (varid == 0U)
|
const Variable *var = tok->astOperand1()->variable();
|
||||||
continue;
|
if (!var || (!var->isLocal() && !var->isArgument()))
|
||||||
const Variable *var = tok->astOperand1()->variable();
|
continue;
|
||||||
if (!var || !var->isLocal())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const Token * const endOfVarScope = var->typeStartToken()->scope()->classEnd;
|
const Token * const endOfVarScope = var->typeStartToken()->scope()->classEnd;
|
||||||
|
|
||||||
// Rhs values..
|
// Rhs values..
|
||||||
if (!tok->astOperand2() || tok->astOperand2()->values.empty())
|
if (!tok->astOperand2() || tok->astOperand2()->values.empty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const std::list<ValueFlow::Value>& values = tok->astOperand2()->values;
|
const std::list<ValueFlow::Value>& values = tok->astOperand2()->values;
|
||||||
const bool constValue = tok->astOperand2()->isNumber();
|
const bool constValue = tok->astOperand2()->isNumber();
|
||||||
valueFlowForward(tok, endOfVarScope, var, varid, values, constValue, tokenlist, errorLogger, settings);
|
valueFlowForward(tok, endOfVarScope, var, varid, values, constValue, tokenlist, errorLogger, settings);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void valueFlowAfterCondition(TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings)
|
static void valueFlowAfterCondition(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings)
|
||||||
{
|
{
|
||||||
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
|
const std::size_t functions = symboldatabase->functionScopes.size();
|
||||||
const Token *vartok, *numtok;
|
for (std::size_t i = 0; i < functions; ++i) {
|
||||||
|
const Scope * scope = symboldatabase->functionScopes[i];
|
||||||
|
for (Token* tok = const_cast<Token*>(scope->classStart); tok != scope->classEnd; tok = tok->next()) {
|
||||||
|
const Token *vartok, *numtok;
|
||||||
|
|
||||||
// Comparison
|
// Comparison
|
||||||
if (Token::Match(tok,"==|!=|>=|<=")) {
|
if (Token::Match(tok, "==|!=|>=|<=")) {
|
||||||
if (!tok->astOperand1() || !tok->astOperand2())
|
if (!tok->astOperand1() || !tok->astOperand2())
|
||||||
continue;
|
continue;
|
||||||
if (tok->astOperand1()->isNumber()) {
|
if (tok->astOperand1()->isNumber()) {
|
||||||
numtok = tok->astOperand1();
|
numtok = tok->astOperand1();
|
||||||
vartok = tok->astOperand2();
|
vartok = tok->astOperand2();
|
||||||
} else {
|
} else {
|
||||||
numtok = tok->astOperand2();
|
numtok = tok->astOperand2();
|
||||||
|
vartok = tok->astOperand1();
|
||||||
|
}
|
||||||
|
if (vartok->str() == "=" && vartok->astOperand1() && vartok->astOperand2())
|
||||||
|
vartok = vartok->astOperand1();
|
||||||
|
if (!vartok->isName() || !numtok->isNumber() || !MathLib::isInt(numtok->str()))
|
||||||
|
continue;
|
||||||
|
} else if (tok->str() == "!") {
|
||||||
vartok = tok->astOperand1();
|
vartok = tok->astOperand1();
|
||||||
|
numtok = nullptr;
|
||||||
|
if (!vartok || !vartok->isName())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
} else if (tok->isName() &&
|
||||||
|
(Token::Match(tok->astParent(), "%oror%|&&") ||
|
||||||
|
Token::Match(tok->tokAt(-2), "if|while ( %var% [)=]"))) {
|
||||||
|
vartok = tok;
|
||||||
|
numtok = nullptr;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (vartok->str() == "=" && vartok->astOperand1() && vartok->astOperand2())
|
|
||||||
vartok = vartok->astOperand1();
|
const unsigned int varid = vartok->varId();
|
||||||
if (!vartok->isName() || !numtok->isNumber() || !MathLib::isInt(numtok->str()))
|
if (varid == 0U)
|
||||||
continue;
|
continue;
|
||||||
} else if (tok->str() == "!") {
|
const Variable *var = vartok->variable();
|
||||||
vartok = tok->astOperand1();
|
if (!var || !(var->isLocal() || var->isArgument()))
|
||||||
numtok = nullptr;
|
|
||||||
if (!vartok || !vartok->isName())
|
|
||||||
continue;
|
continue;
|
||||||
|
std::list<ValueFlow::Value> values;
|
||||||
|
values.push_back(ValueFlow::Value(tok, numtok ? MathLib::toLongNumber(numtok->str()) : 0LL));
|
||||||
|
|
||||||
} else if (tok->isName() &&
|
if (Token::Match(tok->astParent(), "%oror%|&&")) {
|
||||||
(Token::Match(tok->astParent(), "%oror%|&&") ||
|
Token *parent = const_cast<Token*>(tok->astParent());
|
||||||
Token::Match(tok->tokAt(-2), "if|while ( %var% [)=]"))) {
|
const std::string &op(parent->str());
|
||||||
vartok = tok;
|
|
||||||
numtok = nullptr;
|
|
||||||
|
|
||||||
} else {
|
if (parent->astOperand1() == tok &&
|
||||||
continue;
|
((op == "&&" && Token::Match(tok, "==|>=|<=|!")) ||
|
||||||
}
|
(op == "||" && Token::Match(tok, "%name%|!=")))) {
|
||||||
|
bool assign = false;
|
||||||
const unsigned int varid = vartok->varId();
|
for (; !assign && parent && parent->str() == op; parent = const_cast<Token*>(parent->astParent())) {
|
||||||
if (varid == 0U)
|
std::stack<Token *> tokens;
|
||||||
continue;
|
tokens.push(const_cast<Token*>(parent->astOperand2()));
|
||||||
const Variable *var = vartok->variable();
|
while (!tokens.empty()) {
|
||||||
if (!var || !(var->isLocal() || var->isArgument()))
|
Token *rhstok = tokens.top();
|
||||||
continue;
|
tokens.pop();
|
||||||
std::list<ValueFlow::Value> values;
|
if (!rhstok)
|
||||||
values.push_back(ValueFlow::Value(tok, numtok ? MathLib::toLongNumber(numtok->str()) : 0LL));
|
continue;
|
||||||
|
tokens.push(const_cast<Token*>(rhstok->astOperand1()));
|
||||||
if (Token::Match(tok->astParent(), "%oror%|&&")) {
|
tokens.push(const_cast<Token*>(rhstok->astOperand2()));
|
||||||
Token *parent = const_cast<Token*>(tok->astParent());
|
if (rhstok->varId() == varid)
|
||||||
const std::string &op(parent->str());
|
setTokenValue(rhstok, values.front());
|
||||||
|
if (Token::Match(rhstok, "++|--|=") && Token::Match(rhstok->astOperand1(), "%varid%", varid)) {
|
||||||
if (parent->astOperand1() == tok &&
|
assign = true;
|
||||||
((op == "&&" && Token::Match(tok, "==|>=|<=|!")) ||
|
break;
|
||||||
(op == "||" && Token::Match(tok, "%name%|!=")))) {
|
}
|
||||||
bool assign = false;
|
|
||||||
for (; !assign && parent && parent->str() == op; parent = const_cast<Token*>(parent->astParent())) {
|
|
||||||
std::stack<Token *> tokens;
|
|
||||||
tokens.push(const_cast<Token*>(parent->astOperand2()));
|
|
||||||
while (!tokens.empty()) {
|
|
||||||
Token *rhstok = tokens.top();
|
|
||||||
tokens.pop();
|
|
||||||
if (!rhstok)
|
|
||||||
continue;
|
|
||||||
tokens.push(const_cast<Token*>(rhstok->astOperand1()));
|
|
||||||
tokens.push(const_cast<Token*>(rhstok->astOperand2()));
|
|
||||||
if (rhstok->varId() == varid)
|
|
||||||
setTokenValue(rhstok, values.front());
|
|
||||||
if (Token::Match(rhstok, "++|--|=") && Token::Match(rhstok->astOperand1(),"%varid%",varid)) {
|
|
||||||
assign = true;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
while (parent->astParent() && parent == parent->astParent()->astOperand2())
|
||||||
|
parent = const_cast<Token*>(parent->astParent());
|
||||||
}
|
}
|
||||||
while (parent->astParent() && parent == parent->astParent()->astOperand2())
|
|
||||||
parent = const_cast<Token*>(parent->astParent());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const Token *top = tok->astTop();
|
const Token *top = tok->astTop();
|
||||||
if (top && Token::Match(top->previous(), "if|while (") && !top->previous()->isExpandedMacro()) {
|
if (top && Token::Match(top->previous(), "if|while (") && !top->previous()->isExpandedMacro()) {
|
||||||
// does condition reassign variable?
|
// does condition reassign variable?
|
||||||
if (tok != top->astOperand2() &&
|
if (tok != top->astOperand2() &&
|
||||||
Token::Match(top->astOperand2(), "%oror%|&&") &&
|
Token::Match(top->astOperand2(), "%oror%|&&") &&
|
||||||
isVariableChanged(top,top->link(),varid)) {
|
isVariableChanged(top, top->link(), varid)) {
|
||||||
if (settings->debugwarnings)
|
if (settings->debugwarnings)
|
||||||
bailout(tokenlist, errorLogger, tok, "assignment in condition");
|
bailout(tokenlist, errorLogger, tok, "assignment in condition");
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// start token of conditional code
|
|
||||||
Token *startToken = nullptr;
|
|
||||||
|
|
||||||
// based on the comparison, should we check the if or while?
|
|
||||||
int codeblock = 0;
|
|
||||||
if (Token::Match(tok, "==|>=|<=|!"))
|
|
||||||
codeblock = 1;
|
|
||||||
else if (Token::Match(tok, "%name%|!="))
|
|
||||||
codeblock = 2;
|
|
||||||
|
|
||||||
// determine startToken based on codeblock
|
|
||||||
if (codeblock > 0) {
|
|
||||||
// if astParent is "!" we need to invert codeblock
|
|
||||||
const Token *parent = tok->astParent();
|
|
||||||
while (parent && parent->str() == "&&")
|
|
||||||
parent = parent->astParent();
|
|
||||||
if (parent && parent->str() == "!")
|
|
||||||
codeblock = (codeblock == 1) ? 2 : 1;
|
|
||||||
|
|
||||||
// convert codeblock to a startToken
|
|
||||||
if (codeblock == 1 && Token::simpleMatch(top->link(), ") {"))
|
|
||||||
startToken = top->link()->next();
|
|
||||||
else if (Token::simpleMatch(top->link()->linkAt(1), "} else {"))
|
|
||||||
startToken = top->link()->linkAt(1)->tokAt(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ok = true;
|
|
||||||
if (startToken)
|
|
||||||
ok = valueFlowForward(startToken->next(), startToken->link(), var, varid, values, true, tokenlist, errorLogger, settings);
|
|
||||||
|
|
||||||
// After conditional code..
|
|
||||||
if (ok && Token::simpleMatch(top->link(), ") {")) {
|
|
||||||
Token *after = top->link()->linkAt(1);
|
|
||||||
std::string unknownFunction;
|
|
||||||
if (settings->library.isScopeNoReturn(after,&unknownFunction)) {
|
|
||||||
if (settings->debugwarnings && !unknownFunction.empty())
|
|
||||||
bailout(tokenlist, errorLogger, after, "possible noreturn scope");
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isreturn = (codeblock == 1 && isReturn(after));
|
// start token of conditional code
|
||||||
|
Token *startToken = nullptr;
|
||||||
|
|
||||||
if (Token::simpleMatch(after, "} else {")) {
|
// based on the comparison, should we check the if or while?
|
||||||
after = after->linkAt(2);
|
int codeblock = 0;
|
||||||
if (Token::simpleMatch(after->tokAt(-2), ") ; }")) {
|
if (Token::Match(tok, "==|>=|<=|!"))
|
||||||
if (settings->debugwarnings)
|
codeblock = 1;
|
||||||
|
else if (Token::Match(tok, "%name%|!="))
|
||||||
|
codeblock = 2;
|
||||||
|
|
||||||
|
// determine startToken based on codeblock
|
||||||
|
if (codeblock > 0) {
|
||||||
|
// if astParent is "!" we need to invert codeblock
|
||||||
|
const Token *parent = tok->astParent();
|
||||||
|
while (parent && parent->str() == "&&")
|
||||||
|
parent = parent->astParent();
|
||||||
|
if (parent && parent->str() == "!")
|
||||||
|
codeblock = (codeblock == 1) ? 2 : 1;
|
||||||
|
|
||||||
|
// convert codeblock to a startToken
|
||||||
|
if (codeblock == 1 && Token::simpleMatch(top->link(), ") {"))
|
||||||
|
startToken = top->link()->next();
|
||||||
|
else if (Token::simpleMatch(top->link()->linkAt(1), "} else {"))
|
||||||
|
startToken = top->link()->linkAt(1)->tokAt(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ok = true;
|
||||||
|
if (startToken)
|
||||||
|
ok = valueFlowForward(startToken->next(), startToken->link(), var, varid, values, true, tokenlist, errorLogger, settings);
|
||||||
|
|
||||||
|
// After conditional code..
|
||||||
|
if (ok && Token::simpleMatch(top->link(), ") {")) {
|
||||||
|
Token *after = top->link()->linkAt(1);
|
||||||
|
std::string unknownFunction;
|
||||||
|
if (settings->library.isScopeNoReturn(after, &unknownFunction)) {
|
||||||
|
if (settings->debugwarnings && !unknownFunction.empty())
|
||||||
bailout(tokenlist, errorLogger, after, "possible noreturn scope");
|
bailout(tokenlist, errorLogger, after, "possible noreturn scope");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
isreturn |= (codeblock == 2 && isReturn(after));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isreturn) {
|
bool isreturn = (codeblock == 1 && isReturn(after));
|
||||||
// TODO: constValue could be true if there are no assignments in the conditional blocks and
|
|
||||||
// perhaps if there are no && and no || in the condition
|
if (Token::simpleMatch(after, "} else {")) {
|
||||||
bool constValue = false;
|
after = after->linkAt(2);
|
||||||
valueFlowForward(after->next(), top->scope()->classEnd, var, varid, values, constValue, tokenlist, errorLogger, settings);
|
if (Token::simpleMatch(after->tokAt(-2), ") ; }")) {
|
||||||
|
if (settings->debugwarnings)
|
||||||
|
bailout(tokenlist, errorLogger, after, "possible noreturn scope");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
isreturn |= (codeblock == 2 && isReturn(after));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isreturn) {
|
||||||
|
// TODO: constValue could be true if there are no assignments in the conditional blocks and
|
||||||
|
// perhaps if there are no && and no || in the condition
|
||||||
|
bool constValue = false;
|
||||||
|
valueFlowForward(after->next(), top->scope()->classEnd, var, varid, values, constValue, tokenlist, errorLogger, settings);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1512,17 +1522,14 @@ static void valueFlowForLoopSimplifyAfter(Token *fortok, unsigned int varid, con
|
||||||
settings);
|
settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void valueFlowForLoop(TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings)
|
static void valueFlowForLoop(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings)
|
||||||
{
|
{
|
||||||
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
|
for (std::list<Scope>::const_iterator scope = symboldatabase->scopeList.begin(); scope != symboldatabase->scopeList.end(); ++scope) {
|
||||||
if (!Token::simpleMatch(tok, "for (") ||
|
if (scope->type != Scope::eFor)
|
||||||
!Token::simpleMatch(tok->next()->astOperand2(), ";") ||
|
|
||||||
!Token::simpleMatch(tok->next()->astOperand2()->astOperand2(), ";"))
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Token * const bodyStart = tok->linkAt(1)->next();
|
Token* tok = const_cast<Token*>(scope->classDef);
|
||||||
if (!bodyStart || !bodyStart->link() || bodyStart->str() != "{")
|
Token* const bodyStart = const_cast<Token*>(scope->classStart);
|
||||||
continue;
|
|
||||||
|
|
||||||
unsigned int varid(0);
|
unsigned int varid(0);
|
||||||
MathLib::bigint num1(0), num2(0), numAfter(0);
|
MathLib::bigint num1(0), num2(0), numAfter(0);
|
||||||
|
@ -1678,7 +1685,7 @@ static void valueFlowFunctionReturn(TokenList *tokenlist, ErrorLogger *errorLogg
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ValueFlow::setValues(TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings)
|
void ValueFlow::setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings)
|
||||||
{
|
{
|
||||||
for (Token *tok = tokenlist->front(); tok; tok = tok->next())
|
for (Token *tok = tokenlist->front(); tok; tok = tok->next())
|
||||||
tok->values.clear();
|
tok->values.clear();
|
||||||
|
@ -1688,9 +1695,9 @@ void ValueFlow::setValues(TokenList *tokenlist, ErrorLogger *errorLogger, const
|
||||||
valueFlowPointerAlias(tokenlist);
|
valueFlowPointerAlias(tokenlist);
|
||||||
valueFlowFunctionReturn(tokenlist, errorLogger, settings);
|
valueFlowFunctionReturn(tokenlist, errorLogger, settings);
|
||||||
valueFlowBitAnd(tokenlist);
|
valueFlowBitAnd(tokenlist);
|
||||||
valueFlowForLoop(tokenlist, errorLogger, settings);
|
valueFlowForLoop(tokenlist, symboldatabase, errorLogger, settings);
|
||||||
valueFlowBeforeCondition(tokenlist, errorLogger, settings);
|
valueFlowBeforeCondition(tokenlist, symboldatabase, errorLogger, settings);
|
||||||
valueFlowAfterAssign(tokenlist, errorLogger, settings);
|
valueFlowAfterAssign(tokenlist, symboldatabase, errorLogger, settings);
|
||||||
valueFlowAfterCondition(tokenlist, errorLogger, settings);
|
valueFlowAfterCondition(tokenlist, symboldatabase, errorLogger, settings);
|
||||||
valueFlowSubFunction(tokenlist, errorLogger, settings);
|
valueFlowSubFunction(tokenlist, errorLogger, settings);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
|
|
||||||
class Token;
|
class Token;
|
||||||
class TokenList;
|
class TokenList;
|
||||||
|
class SymbolDatabase;
|
||||||
class ErrorLogger;
|
class ErrorLogger;
|
||||||
class Settings;
|
class Settings;
|
||||||
|
|
||||||
|
@ -54,7 +55,7 @@ namespace ValueFlow {
|
||||||
bool inconclusive;
|
bool inconclusive;
|
||||||
};
|
};
|
||||||
|
|
||||||
void setValues(TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings);
|
void setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // valueflowH
|
#endif // valueflowH
|
||||||
|
|
Loading…
Reference in New Issue