Fix 11028: False positive: invalidContainer (#4083)

* Fix 11028: False positive: invalidContainer

* Format
This commit is contained in:
Paul Fultz II 2022-05-04 23:54:36 -05:00 committed by GitHub
parent adba751217
commit 5afd6880c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 58 additions and 54 deletions

View File

@ -1051,6 +1051,8 @@ static const ValueFlow::Value* getInnerLifetime(const Token* tok,
ValueFlow::Value::LifetimeKind::SubObject,
ValueFlow::Value::LifetimeKind::Lambda},
val.lifetimeKind)) {
if (val.isInconclusive())
return nullptr;
if (val.capturetok)
return getInnerLifetime(val.capturetok, id, errorPath, depth - 1);
if (errorPath)

View File

@ -3879,13 +3879,13 @@ private:
}
};
static void valueFlowLifetimeConstructor(Token* tok,
const Function* constructor,
const std::string& name,
std::vector<const Token*> args,
TokenList* tokenlist,
ErrorLogger* errorLogger,
const Settings* settings)
static void valueFlowLifetimeUserConstructor(Token* tok,
const Function* constructor,
const std::string& name,
std::vector<const Token*> args,
TokenList* tokenlist,
ErrorLogger* errorLogger,
const Settings* settings)
{
if (!constructor)
return;
@ -4026,7 +4026,7 @@ static void valueFlowLifetimeFunction(Token *tok, TokenList *tokenlist, ErrorLog
} else if (tok->function()) {
const Function *f = tok->function();
if (f->isConstructor()) {
valueFlowLifetimeConstructor(tok->next(), f, tok->str(), getArguments(tok), tokenlist, errorLogger, settings);
valueFlowLifetimeUserConstructor(tok->next(), f, tok->str(), getArguments(tok), tokenlist, errorLogger, settings);
return;
}
if (Function::returnsReference(f))
@ -4134,11 +4134,11 @@ static const Function* findConstructor(const Scope* scope, const Token* tok, con
return f;
}
static void valueFlowLifetimeConstructor(Token* tok,
const Type* t,
TokenList* tokenlist,
ErrorLogger* errorLogger,
const Settings* settings)
static void valueFlowLifetimeClassConstructor(Token* tok,
const Type* t,
TokenList* tokenlist,
ErrorLogger* errorLogger,
const Settings* settings)
{
if (!Token::Match(tok, "(|{"))
return;
@ -4185,59 +4185,52 @@ static void valueFlowLifetimeConstructor(Token* tok,
});
} else {
const Function* constructor = findConstructor(scope, tok, args);
valueFlowLifetimeConstructor(tok, constructor, t->name(), args, tokenlist, errorLogger, settings);
valueFlowLifetimeUserConstructor(tok, constructor, t->name(), args, tokenlist, errorLogger, settings);
}
}
}
static bool hasInitList(const Token* tok)
{
if (astIsPointer(tok))
return true;
if (astIsContainer(tok)) {
const Library::Container * library = getLibraryContainer(tok);
if (!library)
return false;
return library->hasInitializerListConstructor;
}
return false;
}
static void valueFlowLifetimeConstructor(Token* tok, TokenList* tokenlist, ErrorLogger* errorLogger, const Settings* settings)
{
if (!Token::Match(tok, "(|{"))
return;
if (isScope(tok))
return;
Token* parent = tok->astParent();
while (Token::simpleMatch(parent, ","))
parent = parent->astParent();
if (Token::Match(tok, "{|(") && astIsContainerView(tok) && !tok->function()) {
std::vector<const Token*> args = getArguments(tok);
if (args.size() == 1 && astIsContainerOwned(args.front())) {
LifetimeStore{args.front(), "Passed to container view.", ValueFlow::Value::LifetimeKind::SubObject}.byRef(
tok, tokenlist, errorLogger, settings);
}
} else if (Token::simpleMatch(parent, "{") && hasInitList(parent->astParent())) {
valueFlowLifetimeConstructor(tok, Token::typeOf(parent->previous()), tokenlist, errorLogger, settings);
} else if (Token::simpleMatch(tok, "{") && hasInitList(parent)) {
std::vector<const Token *> args = getArguments(tok);
// Assume range constructor if passed a pair of iterators
if (astIsContainer(parent) && args.size() == 2 && astIsIterator(args[0]) && astIsIterator(args[1])) {
LifetimeStore::forEach(
args, "Passed to initializer list.", ValueFlow::Value::LifetimeKind::SubObject, [&](const LifetimeStore& ls) {
ls.byDerefCopy(tok, tokenlist, errorLogger, settings);
});
std::vector<ValueType> vts;
if (tok->valueType()) {
vts = {*tok->valueType()};
} else if (Token::Match(tok->previous(), "%var% {|(") && isVariableDecl(tok->previous()) &&
tok->previous()->valueType()) {
vts = {*tok->previous()->valueType()};
} else if (Token::simpleMatch(tok, "{") && !Token::Match(tok->previous(), "%name%")) {
vts = getParentValueTypes(tok, settings);
}
for (const ValueType& vt : vts) {
if (vt.container && vt.type == ValueType::CONTAINER) {
std::vector<const Token*> args = getArguments(tok);
if (args.size() == 1 && vt.container->view && astIsContainerOwned(args.front())) {
LifetimeStore{args.front(), "Passed to container view.", ValueFlow::Value::LifetimeKind::SubObject}
.byRef(tok, tokenlist, errorLogger, settings);
} else if (args.size() == 2 && astIsIterator(args[0]) && astIsIterator(args[1])) {
LifetimeStore::forEach(
args,
"Passed to initializer list.",
ValueFlow::Value::LifetimeKind::SubObject,
[&](const LifetimeStore& ls) {
ls.byDerefCopy(tok, tokenlist, errorLogger, settings);
});
} else if (vt.container->hasInitializerListConstructor) {
LifetimeStore::forEach(args,
"Passed to initializer list.",
ValueFlow::Value::LifetimeKind::SubObject,
[&](const LifetimeStore& ls) {
ls.byVal(tok, tokenlist, errorLogger, settings);
});
}
} else {
LifetimeStore::forEach(args,
"Passed to initializer list.",
ValueFlow::Value::LifetimeKind::SubObject,
[&](const LifetimeStore& ls) {
ls.byVal(tok, tokenlist, errorLogger, settings);
});
valueFlowLifetimeClassConstructor(tok, Token::typeOf(tok->previous()), tokenlist, errorLogger, settings);
}
} else {
valueFlowLifetimeConstructor(tok, Token::typeOf(tok->previous()), tokenlist, errorLogger, settings);
}
}

View File

@ -5336,6 +5336,15 @@ private:
ASSERT_EQUALS(
"[test.cpp:6] -> [test.cpp:7] -> [test.cpp:8] -> [test.cpp:5] -> [test.cpp:9]: (error) Using object that points to local variable 'v' that may be invalid.\n",
errout.str());
// #11028
check("void f(std::vector<int> c) {\n"
" std::vector<int> d(c.begin(), c.end());\n"
" c.erase(c.begin());\n"
" d.push_back(0);\n"
"}\n",
true);
ASSERT_EQUALS("", errout.str());
}
void invalidContainerLoop() {