Skip to content

Commit

Permalink
Fix issue 10208: FP: knownConditionTrueFalse in for loop with functio…
Browse files Browse the repository at this point in the history
…n that assigns by ref (danmar#3198)
  • Loading branch information
pfultz2 authored Apr 18, 2021
1 parent 56773b8 commit 563c9dd
Show file tree
Hide file tree
Showing 10 changed files with 99 additions and 19 deletions.
2 changes: 1 addition & 1 deletion gui/threadhandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ void ThreadHandler::initialize(ResultsView *view)
this, &ThreadHandler::bughuntingReportLine);
}

void ThreadHandler::loadSettings(QSettings &settings)
void ThreadHandler::loadSettings(const QSettings &settings)
{
setThreadCount(settings.value(SETTINGS_CHECK_THREADS, 1).toInt());
}
Expand Down
2 changes: 1 addition & 1 deletion gui/threadhandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ class ThreadHandler : public QObject {
* @brief Load settings
* @param settings QSettings to load settings from
*/
void loadSettings(QSettings &settings);
void loadSettings(const QSettings &settings);

/**
* @brief Save settings
Expand Down
28 changes: 21 additions & 7 deletions lib/astutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1774,12 +1774,14 @@ bool isVariableChangedByFunctionCall(const Token *tok, int indirect, const Setti
if (!arg)
continue;
conclusive = true;
if (addressOf || (indirect > 0 && arg->isPointer())) {
if (!arg->isConst())
if (addressOf || indirect > 0) {
if (!arg->isConst() && arg->isPointer())
return true;
// If const is applied to the pointer, then the value can still be modified
if (Token::simpleMatch(arg->typeEndToken(), "* const"))
return true;
if (!arg->isPointer())
return true;
}
if (!arg->isConst() && arg->isReference())
return true;
Expand All @@ -1790,6 +1792,13 @@ bool isVariableChangedByFunctionCall(const Token *tok, int indirect, const Setti
return false;
}

bool isVariableChangedByFunctionCall(const Token* tok, int indirect, const Settings* settings)
{
bool inconclusive = false;
bool r = isVariableChangedByFunctionCall(tok, indirect, settings, &inconclusive);
return r || inconclusive;
}

bool isVariableChanged(const Token *tok, int indirect, const Settings *settings, bool cpp, int depth)
{
if (!tok)
Expand Down Expand Up @@ -1837,13 +1846,17 @@ bool isVariableChanged(const Token *tok, int indirect, const Settings *settings,
const ValueType * valueType = var->valueType();
isConst = (valueType && valueType->pointer == 1 && valueType->constness == 1);
}
if (isConst)
return false;

const Token *ftok = tok->tokAt(2);
if (settings)
return !settings->library.isFunctionConst(ftok);

const Function * fun = ftok->function();
if (!isConst && (!fun || !fun->isConst()))
if (!fun)
return true;
else
return false;
return !fun->isConst();
}

const Token *ftok = tok2;
Expand Down Expand Up @@ -1949,8 +1962,9 @@ Token* findVariableChanged(Token *start, const Token *end, int indirect, const n
if (globalvar && Token::Match(tok, "%name% ("))
// TODO: Is global variable really changed by function call?
return tok;
// Is aliased function call
if (Token::Match(tok, "%var% (") && std::any_of(tok->values().begin(), tok->values().end(), std::mem_fn(&ValueFlow::Value::isLifetimeValue))) {
// Is aliased function call or alias passed to function
if ((Token::Match(tok, "%var% (") || isVariableChangedByFunctionCall(tok, 1, settings)) &&
std::any_of(tok->values().begin(), tok->values().end(), std::mem_fn(&ValueFlow::Value::isLifetimeValue))) {
bool aliased = false;
// If we can't find the expression then assume it was modified
if (!getExprTok())
Expand Down
11 changes: 10 additions & 1 deletion lib/checkautovariables.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -585,7 +585,16 @@ void CheckAutoVariables::checkVarLifetimeScope(const Token * start, const Token
}
if (!isLifetimeBorrowed(tok, mSettings))
continue;
if (var && !var->isLocal() && !var->isArgument() && !isVariableChanged(tok->next(), tok->scope()->bodyEnd, var->declarationId(), var->isGlobal(), mSettings, mTokenizer->isCPP())) {
const Token* nextTok = nextAfterAstRightmostLeaf(tok->astTop());
if (!nextTok)
nextTok = tok->next();
if (var && !var->isLocal() && !var->isArgument() &&
!isVariableChanged(nextTok,
tok->scope()->bodyEnd,
var->declarationId(),
var->isGlobal(),
mSettings,
mTokenizer->isCPP())) {
errorDanglngLifetime(tok2, &val);
break;
}
Expand Down
3 changes: 2 additions & 1 deletion lib/checkcondition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -713,7 +713,8 @@ void CheckCondition::multiCondition2()
});
}
}
if (Token::Match(tok, "%name% (") && isVariablesChanged(tok, tok->linkAt(1), true, varsInCond, mSettings, mTokenizer->isCPP())) {
if (Token::Match(tok, "%name% (") &&
isVariablesChanged(tok, tok->linkAt(1), 0, varsInCond, mSettings, mTokenizer->isCPP())) {
break;
}
if (Token::Match(tok, "%type% (") && nonlocal && isNonConstFunctionCall(tok, mSettings->library)) // non const function call -> bailout if there are nonlocal variables
Expand Down
2 changes: 1 addition & 1 deletion lib/library.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1400,7 +1400,7 @@ bool Library::isFunctionConst(const std::string& functionName, bool pure) const
}
bool Library::isFunctionConst(const Token *ftok) const
{
if (ftok->function() && ftok->function()->isAttributeConst())
if (ftok->function() && ftok->function()->isConst())
return true;
if (isNotLibraryFunction(ftok))
return false;
Expand Down
15 changes: 11 additions & 4 deletions lib/valueflow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6215,7 +6215,9 @@ static void valueFlowIteratorInfer(TokenList *tokenlist, const Settings *setting
}
}

static std::vector<ValueFlow::Value> getInitListSize(const Token* tok, const Library::Container *container)
static std::vector<ValueFlow::Value> getInitListSize(const Token* tok,
const Library::Container* container,
bool known = true)
{
std::vector<const Token*> args = getArguments(tok);
if ((args.size() == 1 && astIsContainer(args[0]) && args[0]->valueType()->container == container) ||
Expand All @@ -6229,7 +6231,8 @@ static std::vector<ValueFlow::Value> getInitListSize(const Token* tok, const Lib
}
ValueFlow::Value value(args.size());
value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE;
value.setKnown();
if (known)
value.setKnown();
return {value};
}

Expand All @@ -6238,6 +6241,7 @@ static void valueFlowContainerSize(TokenList *tokenlist, SymbolDatabase* symbold
std::map<int, std::size_t> static_sizes;
// declaration
for (const Variable *var : symboldatabase->variableList()) {
bool known = true;
if (!var || !var->isLocal() || var->isPointer() || var->isReference() || var->isStatic())
continue;
if (!var->valueType() || !var->valueType()->container)
Expand All @@ -6249,17 +6253,20 @@ static void valueFlowContainerSize(TokenList *tokenlist, SymbolDatabase* symbold
if (!Token::Match(var->nameToken(), "%name% ;") &&
!(Token::Match(var->nameToken(), "%name% {") && Token::simpleMatch(var->nameToken()->next()->link(), "} ;")))
continue;
if (var->nameToken()->astTop() && Token::Match(var->nameToken()->astTop()->previous(), "for|while"))
known = !isVariableChanged(var, settings, true);
if (var->valueType()->container->size_templateArgNo >= 0) {
if (var->dimensions().size() == 1 && var->dimensions().front().known)
static_sizes[var->declarationId()] = var->dimensions().front().num;
continue;
}
std::vector<ValueFlow::Value> values{ValueFlow::Value{0}};
values.back().valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE;
values.back().setKnown();
if (known)
values.back().setKnown();
if (Token::simpleMatch(var->nameToken()->next(), "{")) {
const Token* initList = var->nameToken()->next();
values = getInitListSize(initList, var->valueType()->container);
values = getInitListSize(initList, var->valueType()->container, known);
}
for (const ValueFlow::Value& value : values)
valueFlowContainerForward(var->nameToken()->next(), var, value, tokenlist);
Expand Down
4 changes: 2 additions & 2 deletions test/cfg/kde.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
class k_global_static_testclass1 {};
K_GLOBAL_STATIC(k_global_static_testclass1, k_global_static_testinstance1);

void valid_code(KConfigGroup &cfgGroup)
void valid_code(const KConfigGroup& cfgGroup)
{
k_global_static_testclass1 * pk_global_static_testclass1 = k_global_static_testinstance1;
printf("%p", pk_global_static_testclass1);
Expand All @@ -23,7 +23,7 @@ void valid_code(KConfigGroup &cfgGroup)
if (entryTest) {}
}

void ignoredReturnValue(KConfigGroup & cfgGroup)
void ignoredReturnValue(const KConfigGroup& cfgGroup)
{
// cppcheck-suppress ignoredReturnValue
cfgGroup.readEntry("test", "default");
Expand Down
2 changes: 1 addition & 1 deletion test/cfg/wxwidgets.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ bool invalidFunctionArgBool_wxPGProperty_Hide(wxPGProperty *pg, bool hide, int f
return pg->Hide(hide, flags);
}

wxTextCtrlHitTestResult nullPointer_wxTextCtrl_HitTest(wxTextCtrl &txtCtrl, const wxPoint &pos)
wxTextCtrlHitTestResult nullPointer_wxTextCtrl_HitTest(const wxTextCtrl& txtCtrl, const wxPoint& pos)
{
// no nullPointer-warning is expected
return txtCtrl.HitTest(pos, NULL);
Expand Down
49 changes: 49 additions & 0 deletions test/testcondition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3561,6 +3561,43 @@ class TestCondition : public TestFixture {
" return false;\n"
"}\n");
ASSERT_EQUALS("", errout.str());

// #10208
check("bool GetFirst(std::string &first);\n"
"bool GetNext(std::string &next);\n"
"void g(const std::string& name);\n"
"void f() {\n"
" for (std::string name; name.empty() ? GetFirst(name) : GetNext(name);)\n"
" g(name);\n"
"}\n");
ASSERT_EQUALS("", errout.str());

check("bool GetFirst(std::string &first);\n"
"bool GetNext(std::string &next);\n"
"void g(const std::string& name);\n"
"void f() {\n"
" for (std::string name{}; name.empty() ? GetFirst(name) : GetNext(name);)\n"
" g(name);\n"
"}\n");
ASSERT_EQUALS("", errout.str());

check("bool GetFirst(std::string &first);\n"
"bool GetNext(std::string &next);\n"
"void g(const std::string& name);\n"
"void f() {\n"
" for (std::string name{'a', 'b'}; name.empty() ? GetFirst(name) : GetNext(name);)\n"
" g(name);\n"
"}\n");
ASSERT_EQUALS("", errout.str());

check("bool GetFirst(const std::string &first);\n"
"bool GetNext(const std::string &next);\n"
"void g(const std::string& name);\n"
"void f() {\n"
" for (std::string name; name.empty() ? GetFirst(name) : GetNext(name);)\n"
" g(name);\n"
"}\n");
ASSERT_EQUALS("[test.cpp:5]: (style) Condition 'name.empty()' is always true\n", errout.str());
}

void alwaysTrueInfer() {
Expand Down Expand Up @@ -3947,6 +3984,18 @@ class TestCondition : public TestFixture {
"}\n");
ASSERT_EQUALS("", errout.str());

check("void g(std::function<void()>);\n"
"void f(std::vector<int> v) {\n"
" auto x = [&v] { v.push_back(1); };\n"
" if(v.empty()) {\n"
" g(x);\n"
" }\n"
" if(v.empty())\n"
" return;\n"
" return;\n"
"}\n");
ASSERT_EQUALS("", errout.str());

// do not crash
check("void assign(const MMA& other) {\n"
" if (mPA.cols != other.mPA.cols || mPA.rows != other.mPA.rows)\n"
Expand Down

0 comments on commit 563c9dd

Please sign in to comment.