Fix 10450: regression, FP : Iterator 'iter' from different container 'l' are used together (#3436)

This commit is contained in:
Paul Fultz II 2021-09-05 00:35:33 -05:00 committed by GitHub
parent cd5fa015c1
commit 0335671b35
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 86 additions and 20 deletions

View File

@ -807,7 +807,11 @@ static void followVariableExpressionError(const Token *tok1, const Token *tok2,
errors->push_back(item); errors->push_back(item);
} }
std::vector<ReferenceToken> followAllReferences(const Token* tok, bool inconclusive, ErrorPath errors, int depth) std::vector<ReferenceToken> followAllReferences(const Token* tok,
bool temporary,
bool inconclusive,
ErrorPath errors,
int depth)
{ {
struct ReferenceTokenLess { struct ReferenceTokenLess {
bool operator()(const ReferenceToken& x, const ReferenceToken& y) const { bool operator()(const ReferenceToken& x, const ReferenceToken& y) const {
@ -831,10 +835,11 @@ std::vector<ReferenceToken> followAllReferences(const Token* tok, bool inconclus
} else if (Token::simpleMatch(var->declEndToken(), "=")) { } else if (Token::simpleMatch(var->declEndToken(), "=")) {
errors.emplace_back(var->declEndToken(), "Assigned to reference."); errors.emplace_back(var->declEndToken(), "Assigned to reference.");
const Token *vartok = var->declEndToken()->astOperand2(); const Token *vartok = var->declEndToken()->astOperand2();
if (vartok == tok) if (vartok == tok || (!temporary && isTemporary(true, vartok, nullptr, true) &&
(var->isConst() || var->isRValueReference())))
return {{tok, std::move(errors)}}; return {{tok, std::move(errors)}};
if (vartok) if (vartok)
return followAllReferences(vartok, inconclusive, std::move(errors), depth - 1); return followAllReferences(vartok, temporary, inconclusive, std::move(errors), depth - 1);
} else { } else {
return {{tok, std::move(errors)}}; return {{tok, std::move(errors)}};
} }
@ -844,9 +849,9 @@ std::vector<ReferenceToken> followAllReferences(const Token* tok, bool inconclus
const Token* tok2 = tok->astOperand2(); const Token* tok2 = tok->astOperand2();
std::vector<ReferenceToken> refs; std::vector<ReferenceToken> refs;
refs = followAllReferences(tok2->astOperand1(), inconclusive, errors, depth - 1); refs = followAllReferences(tok2->astOperand1(), temporary, inconclusive, errors, depth - 1);
result.insert(refs.begin(), refs.end()); result.insert(refs.begin(), refs.end());
refs = followAllReferences(tok2->astOperand2(), inconclusive, errors, depth - 1); refs = followAllReferences(tok2->astOperand2(), temporary, inconclusive, errors, depth - 1);
result.insert(refs.begin(), refs.end()); result.insert(refs.begin(), refs.end());
if (!inconclusive && result.size() != 1) if (!inconclusive && result.size() != 1)
@ -865,7 +870,8 @@ std::vector<ReferenceToken> followAllReferences(const Token* tok, bool inconclus
for (const Token* returnTok : returns) { for (const Token* returnTok : returns) {
if (returnTok == tok) if (returnTok == tok)
continue; continue;
for (const ReferenceToken& rt:followAllReferences(returnTok, inconclusive, errors, depth - returns.size())) { for (const ReferenceToken& rt :
followAllReferences(returnTok, temporary, inconclusive, errors, depth - returns.size())) {
const Variable* argvar = rt.token->variable(); const Variable* argvar = rt.token->variable();
if (!argvar) if (!argvar)
return {{tok, std::move(errors)}}; return {{tok, std::move(errors)}};
@ -880,7 +886,8 @@ std::vector<ReferenceToken> followAllReferences(const Token* tok, bool inconclus
ErrorPath er = errors; ErrorPath er = errors;
er.emplace_back(returnTok, "Return reference."); er.emplace_back(returnTok, "Return reference.");
er.emplace_back(tok->previous(), "Called function passing '" + argTok->expressionString() + "'."); er.emplace_back(tok->previous(), "Called function passing '" + argTok->expressionString() + "'.");
std::vector<ReferenceToken> refs = followAllReferences(argTok, inconclusive, std::move(er), depth - returns.size()); std::vector<ReferenceToken> refs =
followAllReferences(argTok, temporary, inconclusive, std::move(er), depth - returns.size());
result.insert(refs.begin(), refs.end()); result.insert(refs.begin(), refs.end());
if (!inconclusive && result.size() > 1) if (!inconclusive && result.size() > 1)
return {{tok, std::move(errors)}}; return {{tok, std::move(errors)}};
@ -898,7 +905,7 @@ const Token* followReferences(const Token* tok, ErrorPath* errors)
{ {
if (!tok) if (!tok)
return nullptr; return nullptr;
std::vector<ReferenceToken> refs = followAllReferences(tok, false); std::vector<ReferenceToken> refs = followAllReferences(tok, true, false);
if (refs.size() == 1) { if (refs.size() == 1) {
if (errors) if (errors)
*errors = refs.front().errors; *errors = refs.front().errors;

View File

@ -145,7 +145,11 @@ struct ReferenceToken {
ErrorPath errors; ErrorPath errors;
}; };
std::vector<ReferenceToken> followAllReferences(const Token* tok, bool inconclusive = true, ErrorPath errors = ErrorPath{}, int depth = 20); std::vector<ReferenceToken> followAllReferences(const Token* tok,
bool temporary = true,
bool inconclusive = true,
ErrorPath errors = ErrorPath{},
int depth = 20);
const Token* followReferences(const Token* tok, ErrorPath* errors = nullptr); const Token* followReferences(const Token* tok, ErrorPath* errors = nullptr);
bool isSameExpression(bool cpp, bool macro, const Token *tok1, const Token *tok2, const Library& library, bool pure, bool followVar, ErrorPath* errors=nullptr); bool isSameExpression(bool cpp, bool macro, const Token *tok1, const Token *tok2, const Library& library, bool pure, bool followVar, ErrorPath* errors=nullptr);

View File

@ -3917,21 +3917,53 @@ static void valueFlowLifetime(TokenList *tokenlist, SymbolDatabase*, ErrorLogger
isContainerOfPointers = vt.pointer > 0; isContainerOfPointers = vt.pointer > 0;
} }
LifetimeStore ls; ValueFlow::Value master;
master.valueType = ValueFlow::Value::ValueType::LIFETIME;
master.lifetimeScope = ValueFlow::Value::LifetimeScope::Local;
if (astIsIterator(parent->tokAt(2))) if (astIsIterator(parent->tokAt(2))) {
ls = LifetimeStore{tok, "Iterator to container is created here.", ValueFlow::Value::LifetimeKind::Iterator}; master.errorPath.emplace_back(parent->tokAt(2), "Iterator to container is created here.");
else if ((astIsPointer(parent->tokAt(2)) && !isContainerOfPointers) || Token::Match(parent->next(), "data|c_str")) master.lifetimeKind = ValueFlow::Value::LifetimeKind::Iterator;
ls = LifetimeStore{tok, "Pointer to container is created here.", ValueFlow::Value::LifetimeKind::Object}; } else if ((astIsPointer(parent->tokAt(2)) && !isContainerOfPointers) ||
else Token::Match(parent->next(), "data|c_str")) {
master.errorPath.emplace_back(parent->tokAt(2), "Pointer to container is created here.");
master.lifetimeKind = ValueFlow::Value::LifetimeKind::Object;
} else {
continue; continue;
}
// Dereferencing std::vector<const Token*> toks = {};
if (tok->isUnaryOp("*") || parent->originalName() == "->") if (tok->isUnaryOp("*") || parent->originalName() == "->") {
ls.byDerefCopy(parent->tokAt(2), tokenlist, errorLogger, settings); for (const ValueFlow::Value& v : tok->values()) {
else if (!v.isSymbolicValue())
ls.byRef(parent->tokAt(2), tokenlist, errorLogger, settings); continue;
if (v.isKnown())
continue;
if (v.intvalue != 0)
continue;
if (!v.tokvalue)
continue;
toks.push_back(v.tokvalue);
}
} else {
toks = {tok};
}
for (const Token* tok2 : toks) {
for (const ReferenceToken& rt : followAllReferences(tok2, false)) {
ValueFlow::Value value = master;
value.tokvalue = rt.token;
value.errorPath.insert(value.errorPath.begin(), rt.errors.begin(), rt.errors.end());
setTokenValue(parent->tokAt(2), value, tokenlist->getSettings());
if (!rt.token->variable()) {
LifetimeStore ls = LifetimeStore{
rt.token, master.errorPath.back().second, ValueFlow::Value::LifetimeKind::Object};
ls.byRef(parent->tokAt(2), tokenlist, errorLogger, settings);
}
}
}
valueFlowForwardLifetime(parent->tokAt(2), tokenlist, errorLogger, settings);
} }
// Check constructors // Check constructors
else if (Token::Match(tok, "=|return|%type%|%var% {")) { else if (Token::Match(tok, "=|return|%type%|%var% {")) {

View File

@ -70,6 +70,7 @@ private:
TEST_CASE(iterator25); // #9742 TEST_CASE(iterator25); // #9742
TEST_CASE(iterator26); // #9176 TEST_CASE(iterator26); // #9176
TEST_CASE(iterator27); // #10378 TEST_CASE(iterator27); // #10378
TEST_CASE(iterator28); // #10450
TEST_CASE(iteratorExpression); TEST_CASE(iteratorExpression);
TEST_CASE(iteratorSameExpression); TEST_CASE(iteratorSameExpression);
TEST_CASE(mismatchingContainerIterator); TEST_CASE(mismatchingContainerIterator);
@ -1487,6 +1488,28 @@ private:
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
} }
void iterator28()
{
// #10450
check("struct S {\n"
" struct Private {\n"
" std::list<int> l;\n"
" };\n"
" std::unique_ptr<Private> p;\n"
" int foo();\n"
"};\n"
"int S::foo() {\n"
" for(auto iter = p->l.begin(); iter != p->l.end(); ++iter) {\n"
" if(*iter == 1) {\n"
" p->l.erase(iter);\n"
" return 1;\n"
" }\n"
" }\n"
" return 0;\n"
"}\n");
ASSERT_EQUALS("", errout.str());
}
void iteratorExpression() { void iteratorExpression() {
check("std::vector<int>& f();\n" check("std::vector<int>& f();\n"
"std::vector<int>& g();\n" "std::vector<int>& g();\n"