Fixed #7668 (ValueFlow: return value from abs)
This commit is contained in:
parent
0e9bf9f0c6
commit
fae4a4dacf
|
@ -10,6 +10,7 @@
|
||||||
<pure/>
|
<pure/>
|
||||||
<noreturn>false</noreturn>
|
<noreturn>false</noreturn>
|
||||||
<leak-ignore/>
|
<leak-ignore/>
|
||||||
|
<returnValue>arg1>0?arg1:-arg1</returnValue>
|
||||||
<arg nr="1">
|
<arg nr="1">
|
||||||
<not-uninit/>
|
<not-uninit/>
|
||||||
<not-bool/>
|
<not-bool/>
|
||||||
|
@ -1584,6 +1585,7 @@ The obsolete function 'gets' is called. With 'gets' you'll get a buffer overrun
|
||||||
<pure/>
|
<pure/>
|
||||||
<noreturn>false</noreturn>
|
<noreturn>false</noreturn>
|
||||||
<leak-ignore/>
|
<leak-ignore/>
|
||||||
|
<returnValue>arg1>='0' && arg1<='9'</returnValue>
|
||||||
<arg nr="1">
|
<arg nr="1">
|
||||||
<not-uninit/>
|
<not-uninit/>
|
||||||
<valid>0:255</valid>
|
<valid>0:255</valid>
|
||||||
|
@ -3034,6 +3036,7 @@ The obsolete function 'gets' is called. With 'gets' you'll get a buffer overrun
|
||||||
<pure/>
|
<pure/>
|
||||||
<noreturn>false</noreturn>
|
<noreturn>false</noreturn>
|
||||||
<leak-ignore/>
|
<leak-ignore/>
|
||||||
|
<returnValue>strlen(arg1)</returnValue>
|
||||||
<arg nr="1">
|
<arg nr="1">
|
||||||
<not-null/>
|
<not-null/>
|
||||||
<not-uninit/>
|
<not-uninit/>
|
||||||
|
|
|
@ -545,6 +545,8 @@ Library::Error Library::loadFunction(const tinyxml2::XMLElement * const node, co
|
||||||
leakignore.insert(name);
|
leakignore.insert(name);
|
||||||
else if (functionnodename == "use-retval")
|
else if (functionnodename == "use-retval")
|
||||||
_useretval.insert(name);
|
_useretval.insert(name);
|
||||||
|
else if (functionnodename == "returnValue" && functionnode->GetText())
|
||||||
|
_returnValue[name] = functionnode->GetText();
|
||||||
else if (functionnodename == "arg") {
|
else if (functionnodename == "arg") {
|
||||||
const char* argNrString = functionnode->Attribute("nr");
|
const char* argNrString = functionnode->Attribute("nr");
|
||||||
if (!argNrString)
|
if (!argNrString)
|
||||||
|
@ -985,6 +987,14 @@ bool Library::isUseRetVal(const Token* ftok) const
|
||||||
_useretval.find(getFunctionName(ftok)) != _useretval.end());
|
_useretval.find(getFunctionName(ftok)) != _useretval.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Library::returnValue(const Token *ftok) const
|
||||||
|
{
|
||||||
|
if (isNotLibraryFunction(ftok))
|
||||||
|
return std::string();
|
||||||
|
std::map<std::string, std::string>::const_iterator it = _returnValue.find(getFunctionName(ftok));
|
||||||
|
return it != _returnValue.end() ? it->second : std::string();
|
||||||
|
}
|
||||||
|
|
||||||
bool Library::isnoreturn(const Token *ftok) const
|
bool Library::isnoreturn(const Token *ftok) const
|
||||||
{
|
{
|
||||||
if (ftok->function() && ftok->function()->isAttributeNoreturn())
|
if (ftok->function() && ftok->function()->isAttributeNoreturn())
|
||||||
|
|
|
@ -165,6 +165,8 @@ public:
|
||||||
|
|
||||||
bool isUseRetVal(const Token* ftok) const;
|
bool isUseRetVal(const Token* ftok) const;
|
||||||
|
|
||||||
|
std::string returnValue(const Token *ftok) const;
|
||||||
|
|
||||||
bool isnoreturn(const Token *ftok) const;
|
bool isnoreturn(const Token *ftok) const;
|
||||||
bool isnotnoreturn(const Token *ftok) const;
|
bool isnotnoreturn(const Token *ftok) const;
|
||||||
|
|
||||||
|
@ -478,6 +480,7 @@ private:
|
||||||
std::map<std::string, AllocFunc> _dealloc; // deallocation functions
|
std::map<std::string, AllocFunc> _dealloc; // deallocation functions
|
||||||
std::set<std::string> _functions;
|
std::set<std::string> _functions;
|
||||||
std::map<std::string, bool> _noreturn; // is function noreturn?
|
std::map<std::string, bool> _noreturn; // is function noreturn?
|
||||||
|
std::map<std::string, std::string> _returnValue;
|
||||||
std::set<std::string> _ignorefunction; // ignore functions/macros from a library (gtk, qt etc)
|
std::set<std::string> _ignorefunction; // ignore functions/macros from a library (gtk, qt etc)
|
||||||
std::map<std::string, bool> _reporterrors;
|
std::map<std::string, bool> _reporterrors;
|
||||||
std::map<std::string, bool> _processAfterCode;
|
std::map<std::string, bool> _processAfterCode;
|
||||||
|
|
|
@ -407,7 +407,7 @@ static void setTokenValue(Token* tok, const ValueFlow::Value &value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculations..
|
// Calculations..
|
||||||
else if ((parent->isArithmeticalOp() || parent->isComparisonOp() || (parent->tokType() == Token::eBitOp)) &&
|
else if ((parent->isArithmeticalOp() || parent->isComparisonOp() || (parent->tokType() == Token::eBitOp) || (parent->tokType() == Token::eLogicalOp)) &&
|
||||||
parent->astOperand1() &&
|
parent->astOperand1() &&
|
||||||
parent->astOperand2()) {
|
parent->astOperand2()) {
|
||||||
const bool known = ((parent->astOperand1()->values.size() == 1U &&
|
const bool known = ((parent->astOperand1()->values.size() == 1U &&
|
||||||
|
@ -503,11 +503,17 @@ static void setTokenValue(Token* tok, const ValueFlow::Value &value)
|
||||||
setTokenValue(parent, result);
|
setTokenValue(parent, result);
|
||||||
break;
|
break;
|
||||||
case '&':
|
case '&':
|
||||||
result.intvalue = value1->intvalue & value2->intvalue;
|
if (parent->str() == "&")
|
||||||
|
result.intvalue = value1->intvalue & value2->intvalue;
|
||||||
|
else
|
||||||
|
result.intvalue = value1->intvalue && value2->intvalue;
|
||||||
setTokenValue(parent, result);
|
setTokenValue(parent, result);
|
||||||
break;
|
break;
|
||||||
case '|':
|
case '|':
|
||||||
result.intvalue = value1->intvalue | value2->intvalue;
|
if (parent->str() == "|")
|
||||||
|
result.intvalue = value1->intvalue | value2->intvalue;
|
||||||
|
else
|
||||||
|
result.intvalue = value1->intvalue || value2->intvalue;
|
||||||
setTokenValue(parent, result);
|
setTokenValue(parent, result);
|
||||||
break;
|
break;
|
||||||
case '^':
|
case '^':
|
||||||
|
@ -2323,6 +2329,68 @@ static void valueFlowSwitchVariable(TokenList *tokenlist, SymbolDatabase* symbol
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void setTokenValues(Token *tok, const std::list<ValueFlow::Value> &values)
|
||||||
|
{
|
||||||
|
for (std::list<ValueFlow::Value>::const_iterator it = values.begin(); it != values.end(); ++it) {
|
||||||
|
const ValueFlow::Value &value = *it;
|
||||||
|
if (!value.tokvalue)
|
||||||
|
setTokenValue(tok, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void valueFlowLibraryFunction(Token *tok, const std::string &returnValue, const Settings *settings)
|
||||||
|
{
|
||||||
|
std::istringstream istr(returnValue);
|
||||||
|
TokenList tokenList(settings);
|
||||||
|
if (!tokenList.createTokens(istr))
|
||||||
|
return;
|
||||||
|
|
||||||
|
const Token *arg1 = tok->astOperand2();
|
||||||
|
while (arg1 && arg1->str() == ",")
|
||||||
|
arg1 = arg1->astOperand1();
|
||||||
|
if (Token::findsimplematch(tokenList.front(), "arg1") && !arg1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (Token::simpleMatch(tokenList.front(), "strlen ( arg1 )") && arg1) {
|
||||||
|
for (std::list<ValueFlow::Value>::const_iterator it = arg1->values.begin(); it != arg1->values.end(); ++it) {
|
||||||
|
const ValueFlow::Value &value = *it;
|
||||||
|
if (value.tokvalue && value.tokvalue->tokType() == Token::eString) {
|
||||||
|
ValueFlow::Value retval(value); // copy all "inconclusive", "condition", etc attributes
|
||||||
|
// set return value..
|
||||||
|
retval.tokvalue = nullptr;
|
||||||
|
retval.intvalue = Token::getStrLength(value.tokvalue);
|
||||||
|
setTokenValue(tok, retval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// combine operators, set links, etc..
|
||||||
|
for (Token *tok2 = tokenList.front(); tok2; tok2 = tok2->next()) {
|
||||||
|
if (Token::Match(tok2, "[!<>=] =")) {
|
||||||
|
tok2->str(tok2->str() + "=");
|
||||||
|
tok2->deleteNext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Evaluate expression
|
||||||
|
tokenList.createAst();
|
||||||
|
valueFlowNumber(&tokenList);
|
||||||
|
for (Token *tok2 = tokenList.front(); tok2; tok2 = tok2->next()) {
|
||||||
|
if (tok2->str() == "arg1" && arg1) {
|
||||||
|
setTokenValues(tok2, arg1->values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find result..
|
||||||
|
for (const Token *tok2 = tokenList.front(); tok2; tok2 = tok2->next()) {
|
||||||
|
if (!tok2->astParent() && !tok2->values.empty()) {
|
||||||
|
setTokenValues(tok, tok2->values);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void valueFlowSubFunction(TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings)
|
static void valueFlowSubFunction(TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings)
|
||||||
{
|
{
|
||||||
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
|
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
|
||||||
|
@ -2330,8 +2398,13 @@ static void valueFlowSubFunction(TokenList *tokenlist, ErrorLogger *errorLogger,
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const Function * const currentFunction = tok->function();
|
const Function * const currentFunction = tok->function();
|
||||||
if (!currentFunction)
|
if (!currentFunction) {
|
||||||
|
// library function?
|
||||||
|
const std::string returnValue(settings->library.returnValue(tok));
|
||||||
|
if (!returnValue.empty())
|
||||||
|
valueFlowLibraryFunction(tok->next(), returnValue, settings);
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Function scope..
|
// Function scope..
|
||||||
const Scope * const functionScope = currentFunction->functionScope;
|
const Scope * const functionScope = currentFunction->functionScope;
|
||||||
|
|
Loading…
Reference in New Issue