Skip to content

Commit de51ebb

Browse files
authored
Fix 9135: Access of moved variable not detected in loop (#4215)
* Fix 9135: Access of moved variable not detected in loop * Format * Fix issue with pushing back on container * Format * Fix null pointer * Remove yeild for now
1 parent ef33ff6 commit de51ebb

7 files changed

Lines changed: 93 additions & 13 deletions

File tree

lib/astutils.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,25 @@ bool astIsContainerOwned(const Token* tok) {
260260
return astIsContainer(tok) && !astIsContainerView(tok);
261261
}
262262

263+
static const Token* getContainerFunction(const Token* tok)
264+
{
265+
if (!tok || !tok->valueType() || !tok->valueType()->container)
266+
return nullptr;
267+
const Token* parent = tok->astParent();
268+
if (Token::Match(parent, ". %name% (") && astIsLHS(tok)) {
269+
return parent->next();
270+
}
271+
return nullptr;
272+
}
273+
274+
Library::Container::Action astContainerAction(const Token* tok)
275+
{
276+
const Token* ftok = getContainerFunction(tok);
277+
if (!ftok)
278+
return Library::Container::Action::NO_ACTION;
279+
return tok->valueType()->container->getAction(ftok->str());
280+
}
281+
263282
bool astIsRangeBasedForDecl(const Token* tok)
264283
{
265284
return Token::simpleMatch(tok->astParent(), ":") && Token::simpleMatch(tok->astParent()->astParent(), "(");

lib/astutils.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,8 @@ bool astIsContainer(const Token *tok);
139139
bool astIsContainerView(const Token* tok);
140140
bool astIsContainerOwned(const Token* tok);
141141

142+
Library::Container::Action astContainerAction(const Token* tok);
143+
142144
/** Is given token a range-declaration in a range-based for loop */
143145
bool astIsRangeBasedForDecl(const Token* tok);
144146

lib/forwardanalyzer.cpp

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -607,15 +607,23 @@ struct ForwardTraversal {
607607
} else if (condTok->values().front().intvalue == inElse) {
608608
return Break();
609609
}
610-
// Handle for loop
611-
Token* stepTok = getStepTokFromEnd(tok);
612-
bool checkThen, checkElse;
613-
std::tie(checkThen, checkElse) = evalCond(condTok);
614-
if (stepTok && !checkElse) {
615-
if (updateRecursive(stepTok) == Progress::Break)
616-
return Break();
617-
if (updateRecursive(condTok) == Progress::Break)
618-
return Break();
610+
// Handle loop
611+
if (inLoop) {
612+
Token* stepTok = getStepTokFromEnd(tok);
613+
bool checkThen, checkElse;
614+
std::tie(checkThen, checkElse) = evalCond(condTok);
615+
if (stepTok && !checkElse) {
616+
if (updateRecursive(stepTok) == Progress::Break)
617+
return Break();
618+
if (updateRecursive(condTok) == Progress::Break)
619+
return Break();
620+
// Reevaluate condition
621+
std::tie(checkThen, checkElse) = evalCond(condTok);
622+
}
623+
if (!checkElse) {
624+
if (updateLoopExit(end, tok, condTok, nullptr, stepTok) == Progress::Break)
625+
return Break();
626+
}
619627
}
620628
analyzer->assume(condTok, !inElse, Analyzer::Assume::Quiet);
621629
if (Token::simpleMatch(tok, "} else {"))
@@ -852,7 +860,6 @@ struct ForwardTraversal {
852860
return nullptr;
853861
return getStepTok(end->link());
854862
}
855-
856863
};
857864

858865
Analyzer::Result valueFlowGenericForward(Token* start, const Token* end, const ValuePtr<Analyzer>& a, const Settings* settings)

lib/valueflow.cpp

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2108,6 +2108,24 @@ static bool evalAssignment(Value& lhsValue, const std::string& assign, const Val
21082108
return !error;
21092109
}
21102110

2111+
static ValueFlow::Value::MoveKind isMoveOrForward(const Token* tok)
2112+
{
2113+
if (!tok)
2114+
return ValueFlow::Value::MoveKind::NonMovedVariable;
2115+
const Token* parent = tok->astParent();
2116+
if (!Token::simpleMatch(parent, "("))
2117+
return ValueFlow::Value::MoveKind::NonMovedVariable;
2118+
const Token* ftok = parent->astOperand1();
2119+
if (!ftok)
2120+
return ValueFlow::Value::MoveKind::NonMovedVariable;
2121+
if (Token::simpleMatch(ftok->astOperand1(), "std :: move"))
2122+
return ValueFlow::Value::MoveKind::MovedVariable;
2123+
if (Token::simpleMatch(ftok->astOperand1(), "std :: forward"))
2124+
return ValueFlow::Value::MoveKind::ForwardedVariable;
2125+
// TODO: Check for cast
2126+
return ValueFlow::Value::MoveKind::NonMovedVariable;
2127+
}
2128+
21112129
template<class T>
21122130
struct SingleRange {
21132131
T* x;
@@ -2416,6 +2434,19 @@ struct ValueFlowAnalyzer : Analyzer {
24162434

24172435
virtual Action isModified(const Token* tok) const {
24182436
Action read = Action::Read;
2437+
const ValueFlow::Value* value = getValue(tok);
2438+
if (value) {
2439+
// Moving a moved value wont change the moved value
2440+
if (value->isMovedValue() && isMoveOrForward(tok) != ValueFlow::Value::MoveKind::NonMovedVariable)
2441+
return read;
2442+
// Inserting elements to container wont change the lifetime
2443+
if (astIsContainer(tok) && value->isLifetimeValue() &&
2444+
contains({Library::Container::Action::PUSH,
2445+
Library::Container::Action::INSERT,
2446+
Library::Container::Action::CHANGE_INTERNAL},
2447+
astContainerAction(tok)))
2448+
return read;
2449+
}
24192450
bool inconclusive = false;
24202451
if (isVariableChangedByFunctionCall(tok, getIndirect(tok), getSettings(), &inconclusive))
24212452
return read | Action::Invalid;
@@ -2424,7 +2455,6 @@ struct ValueFlowAnalyzer : Analyzer {
24242455
if (isVariableChanged(tok, getIndirect(tok), getSettings(), isCPP())) {
24252456
if (Token::Match(tok->astParent(), "*|[|.|++|--"))
24262457
return read | Action::Invalid;
2427-
const ValueFlow::Value* value = getValue(tok);
24282458
// Check if its assigned to the same value
24292459
if (value && !value->isImpossible() && Token::simpleMatch(tok->astParent(), "=") && astIsLHS(tok) &&
24302460
astIsIntegral(tok->astParent()->astOperand2(), false)) {

test/testautovariables.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3943,7 +3943,9 @@ class TestAutoVariables : public TestFixture {
39433943
" if (former_hover != pitem)\n"
39443944
" dosth();\n"
39453945
"}");
3946-
ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4] -> [test.cpp:7]: (error) Using pointer to local variable 'item' that is out of scope.\n", errout.str());
3946+
ASSERT_EQUALS(
3947+
"[test.cpp:5] -> [test.cpp:3] -> [test.cpp:4] -> [test.cpp:7]: (error) Using pointer to local variable 'item' that is out of scope.\n",
3948+
errout.str());
39473949

39483950
// #6575
39493951
check("void trp_deliver_signal() {\n"

test/testbufferoverrun.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -867,7 +867,7 @@ class TestBufferOverrun : public TestFixture {
867867
" i=4;\n"
868868
" }\n"
869869
"}");
870-
ASSERT_EQUALS("", errout.str());
870+
ASSERT_EQUALS("[test.cpp:6]: (error) Array 'a[5]' accessed at index 5, which is out of bounds.\n", errout.str());
871871

872872
check("void f()\n"
873873
"{\n"

test/testother.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ class TestOther : public TestFixture {
254254
TEST_CASE(moveAndAddressOf);
255255
TEST_CASE(partiallyMoved);
256256
TEST_CASE(moveAndLambda);
257+
TEST_CASE(moveInLoop);
257258
TEST_CASE(forwardAndUsed);
258259

259260
TEST_CASE(funcArgNamesDifferent);
@@ -9837,6 +9838,25 @@ class TestOther : public TestFixture {
98379838
ASSERT_EQUALS("", errout.str());
98389839
}
98399840

9841+
void moveInLoop()
9842+
{
9843+
check("void g(std::string&& s);\n"
9844+
"void f() {\n"
9845+
" std::string p;\n"
9846+
" while(true)\n"
9847+
" g(std::move(p));\n"
9848+
"}\n");
9849+
ASSERT_EQUALS("[test.cpp:5]: (warning) Access of moved variable 'p'.\n", errout.str());
9850+
9851+
check("std::list<int> g(std::list<int>&&);\n"
9852+
"void f(std::list<int>l) {\n"
9853+
" for(int i = 0; i < 10; ++i) {\n"
9854+
" for (auto &j : g(std::move(l))) { (void)j; }\n"
9855+
" }\n"
9856+
"}\n");
9857+
ASSERT_EQUALS("[test.cpp:4]: (warning) Access of moved variable 'l'.\n", errout.str());
9858+
}
9859+
98409860
void forwardAndUsed() {
98419861
Settings keepTemplates;
98429862
keepTemplates.checkUnusedTemplates = true;

0 commit comments

Comments
 (0)