Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions lib/library.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1651,3 +1651,50 @@ Library::TypeCheck Library::getTypeCheck(const std::string &check, const std::st
auto it = mTypeChecks.find(std::pair<std::string, std::string>(check, typeName));
return it == mTypeChecks.end() ? TypeCheck::def : it->second;
}

std::shared_ptr<Token> createTokenFromExpression(const std::string& returnValue,
const Settings* settings,
std::unordered_map<nonneg int, const Token*>* lookupVarId)
{
std::shared_ptr<TokenList> tokenList = std::make_shared<TokenList>(settings);
{
const std::string code = "return " + returnValue + ";";
std::istringstream istr(code);
if (!tokenList->createTokens(istr))
return nullptr;
}

// combine operators, set links, etc..
std::stack<Token*> lpar;
for (Token* tok2 = tokenList->front(); tok2; tok2 = tok2->next()) {
if (Token::Match(tok2, "[!<>=] =")) {
tok2->str(tok2->str() + "=");
tok2->deleteNext();
} else if (tok2->str() == "(")
lpar.push(tok2);
else if (tok2->str() == ")") {
if (lpar.empty())
return nullptr;
Token::createMutualLinks(lpar.top(), tok2);
lpar.pop();
}
}
if (!lpar.empty())
return nullptr;

// set varids
for (Token* tok2 = tokenList->front(); tok2; tok2 = tok2->next()) {
if (tok2->str().compare(0, 3, "arg") != 0)
continue;
nonneg int id = std::atoi(tok2->str().c_str() + 3);
tok2->varId(id);
if (lookupVarId)
(*lookupVarId)[id] = tok2;
}

// Evaluate expression
tokenList->createAst();
Token* expr = tokenList->front()->astOperand1();
ValueFlow::valueFlowConstantFoldAST(expr, settings);
return {tokenList, expr};
}
7 changes: 7 additions & 0 deletions lib/library.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,16 @@

#include <cstddef>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>

class Token;
class TokenList;
class Settings;

namespace tinyxml2 {
class XMLDocument;
Expand Down Expand Up @@ -650,6 +653,10 @@ class CPPCHECKLIB Library {

CPPCHECKLIB const Library::Container * getLibraryContainer(const Token * tok);

std::shared_ptr<Token> createTokenFromExpression(const std::string& returnValue,
const Settings* settings,
std::unordered_map<nonneg int, const Token*>* lookupVarId = nullptr);

/// @}
//---------------------------------------------------------------------------
#endif // libraryH
58 changes: 58 additions & 0 deletions lib/programmemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -730,6 +730,36 @@ static ValueFlow::Value executeImpl(const Token* expr, ProgramMemory& pm, const
}

if (Token::Match(expr->previous(), ">|%name% {|(")) {
const Token* ftok = expr->previous();
const Function* f = ftok->function();
// TODO: Evaluate inline functions as well
if (!f && settings && expr->str() == "(") {
std::unordered_map<nonneg int, ValueFlow::Value> args;
int argn = 0;
for (const Token* tok : getArguments(expr)) {
ValueFlow::Value result = execute(tok, pm, settings);
if (!result.isUninitValue())
args[argn] = result;
argn++;
}
// strlen is a special builtin
if (Token::simpleMatch(ftok, "strlen")) {
if (args.count(0) > 0) {
ValueFlow::Value v = args.at(0);
if (v.isTokValue() && v.tokvalue->tokType() == Token::eString) {
v.valueType = ValueFlow::Value::ValueType::INT;
v.intvalue = Token::getStrLength(v.tokvalue);
v.tokvalue = nullptr;
return v;
}
}
} else {
const std::string& returnValue = settings->library.returnValue(ftok);
if (!returnValue.empty())
return evaluateLibraryFunction(args, returnValue, settings);
}
}
// Check if functon modifies argument
visitAstNodes(expr->astOperand2(), [&](const Token* child) {
if (child->exprId() > 0 && pm.hasValue(child->exprId())) {
ValueFlow::Value& v = pm.at(child->exprId());
Expand Down Expand Up @@ -774,6 +804,34 @@ static ValueFlow::Value execute(const Token* expr, ProgramMemory& pm, const Sett
return v;
}

ValueFlow::Value evaluateLibraryFunction(const std::unordered_map<nonneg int, ValueFlow::Value>& args,
const std::string& returnValue,
const Settings* settings)
{
static std::unordered_map<std::string,
std::function<ValueFlow::Value(const std::unordered_map<nonneg int, ValueFlow::Value>& arg)>>
functions = {};
if (functions.count(returnValue) == 0) {

std::unordered_map<nonneg int, const Token*> lookupVarId;
std::shared_ptr<Token> expr = createTokenFromExpression(returnValue, settings, &lookupVarId);

functions[returnValue] =
[lookupVarId, expr, settings](const std::unordered_map<nonneg int, ValueFlow::Value>& xargs) {
if (!expr)
return ValueFlow::Value::unknown();
ProgramMemory pm{};
for (const auto& p : xargs) {
auto it = lookupVarId.find(p.first);
if (it != lookupVarId.end())
pm.setValue(it->second, p.second);
}
return execute(expr.get(), pm, settings);
};
}
return functions.at(returnValue)(args);
}

void execute(const Token* expr,
ProgramMemory* const programMemory,
MathLib::bigint* result,
Expand Down
4 changes: 4 additions & 0 deletions lib/programmemory.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,10 @@ ProgramMemory getProgramMemory(const Token* tok, const Token* expr, const ValueF

ProgramMemory getProgramMemory(const Token *tok, const ProgramMemory::Map& vars);

ValueFlow::Value evaluateLibraryFunction(const std::unordered_map<nonneg int, ValueFlow::Value>& args,
const std::string& returnValue,
const Settings* settings);

#endif


Expand Down
64 changes: 9 additions & 55 deletions lib/valueflow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6759,69 +6759,23 @@ static void valueFlowLibraryFunction(Token *tok, const std::string &returnValue,
}
if (returnValue.find("arg") != std::string::npos && argValues.empty())
return;

TokenList tokenList(settings);
{
const std::string code = "return " + returnValue + ";";
std::istringstream istr(code);
if (!tokenList.createTokens(istr))
return;
}

// combine operators, set links, etc..
std::stack<Token *> lpar;
for (Token *tok2 = tokenList.front(); tok2; tok2 = tok2->next()) {
if (Token::Match(tok2, "[!<>=] =")) {
tok2->str(tok2->str() + "=");
tok2->deleteNext();
} else if (tok2->str() == "(")
lpar.push(tok2);
else if (tok2->str() == ")") {
if (lpar.empty())
return;
Token::createMutualLinks(lpar.top(), tok2);
lpar.pop();
}
}
if (!lpar.empty())
return;

// set varids
std::unordered_map<nonneg int, const Token*> lookupVarId;
for (Token* tok2 = tokenList.front(); tok2; tok2 = tok2->next()) {
if (tok2->str().compare(0, 3, "arg") != 0)
continue;
nonneg int id = std::atoi(tok2->str().c_str() + 3);
tok2->varId(id);
lookupVarId[id] = tok2;
}

// Evaluate expression
tokenList.createAst();
Token* expr = tokenList.front()->astOperand1();
ValueFlow::valueFlowConstantFoldAST(expr, settings);

productParams(argValues, [&](const std::unordered_map<nonneg int, ValueFlow::Value>& arg) {
ProgramMemory pm{};
for (const auto& p : arg) {
const Token* varTok = lookupVarId[p.first];
pm.setValue(varTok, p.second);
}
MathLib::bigint result = 0;
bool error = false;
execute(expr, &pm, &result, &error);
if (error)
ValueFlow::Value value = evaluateLibraryFunction(arg, returnValue, settings);
if (value.isUninitValue())
return;
ValueFlow::Value value(result);
value.setKnown();
ValueFlow::Value::ValueKind kind = ValueFlow::Value::ValueKind::Known;
for (auto&& p : arg) {
if (p.second.isPossible())
value.setPossible();
kind = p.second.valueKind;
if (p.second.isInconclusive()) {
value.setInconclusive();
kind = p.second.valueKind;
break;
}
}
if (value.isImpossible() && kind != ValueFlow::Value::ValueKind::Known)
return;
if (!value.isImpossible())
value.valueKind = kind;
setTokenValue(tok, value, settings);
});
}
Expand Down
12 changes: 12 additions & 0 deletions test/testcondition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4017,6 +4017,18 @@ class TestCondition : public TestFixture {
" }\n"
"}\n");
ASSERT_EQUALS("[test.cpp:5]: (style) Condition 'w' is always true\n", errout.str());

check("void f() {\n"
" if(strlen(\"abc\") == 3) {;}\n"
" if(strlen(\"abc\") == 1) {;}\n"
" if(wcslen(L\"abc\") == 3) {;}\n"
" if(wcslen(L\"abc\") == 1) {;}\n"
"}\n");
ASSERT_EQUALS("[test.cpp:2]: (style) Condition 'strlen(\"abc\")==3' is always true\n"
"[test.cpp:3]: (style) Condition 'strlen(\"abc\")==1' is always false\n"
"[test.cpp:4]: (style) Condition 'wcslen(L\"abc\")==3' is always true\n"
"[test.cpp:5]: (style) Condition 'wcslen(L\"abc\")==1' is always false\n",
errout.str());
}

void alwaysTrueSymbolic()
Expand Down