Fix issue 9930: valueFlowLifetime hang

This commit is contained in:
Paul 2020-10-03 11:01:53 +02:00 committed by Daniel Marjamäki
parent c373be0b16
commit 828a5e2326
2 changed files with 159 additions and 61 deletions

View File

@ -3387,18 +3387,54 @@ struct LifetimeStore {
ValueFlow::Value::LifetimeKind type; ValueFlow::Value::LifetimeKind type;
ErrorPath errorPath; ErrorPath errorPath;
bool inconclusive; bool inconclusive;
bool forward;
struct Context {
Token* tok;
TokenList* tokenlist;
ErrorLogger* errorLogger;
const Settings* settings;
};
LifetimeStore() LifetimeStore()
: argtok(nullptr), message(), type(), errorPath(), inconclusive(false) : argtok(nullptr), message(), type(), errorPath(), inconclusive(false), forward(true), mContext(nullptr)
{} {}
LifetimeStore(const Token *argtok, LifetimeStore(const Token* argtok,
const std::string &message, const std::string& message,
ValueFlow::Value::LifetimeKind type = ValueFlow::Value::LifetimeKind::Object, ValueFlow::Value::LifetimeKind type = ValueFlow::Value::LifetimeKind::Object,
bool inconclusive = false) bool inconclusive = false)
: argtok(argtok), message(message), type(type), errorPath(), inconclusive(inconclusive) : argtok(argtok),
message(message),
type(type),
errorPath(),
inconclusive(inconclusive),
forward(true),
mContext(nullptr)
{} {}
template <class F>
static void forEach(const std::vector<const Token*>& argtoks,
const std::string& message,
ValueFlow::Value::LifetimeKind type,
F f)
{
std::map<const Token*, Context> forwardToks;
for (const Token* arg : argtoks) {
LifetimeStore ls{arg, message, type};
Context c{};
ls.mContext = &c;
ls.forward = false;
f(ls);
if (c.tok)
forwardToks[c.tok] = c;
}
for (const auto& p : forwardToks) {
const Context& c = p.second;
valueFlowForwardLifetime(c.tok, c.tokenlist, c.errorLogger, c.settings);
}
}
static LifetimeStore fromFunctionArg(const Function * f, Token *tok, const Variable *var, TokenList *tokenlist, ErrorLogger *errorLogger) { static LifetimeStore fromFunctionArg(const Function * f, Token *tok, const Variable *var, TokenList *tokenlist, ErrorLogger *errorLogger) {
if (!var) if (!var)
return LifetimeStore{}; return LifetimeStore{};
@ -3423,18 +3459,20 @@ struct LifetimeStore {
} }
template <class Predicate> template <class Predicate>
void byRef(Token *tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings, Predicate pred) const { bool byRef(Token* tok, TokenList* tokenlist, ErrorLogger* errorLogger, const Settings* settings, Predicate pred) const
{
if (!argtok) if (!argtok)
return; return false;
bool update = false;
for (const LifetimeToken& lt : getLifetimeTokens(argtok)) { for (const LifetimeToken& lt : getLifetimeTokens(argtok)) {
if (!settings->inconclusive && lt.inconclusive) if (!settings->inconclusive && lt.inconclusive)
continue; continue;
ErrorPath er = errorPath; ErrorPath er = errorPath;
er.insert(er.end(), lt.errorPath.begin(), lt.errorPath.end()); er.insert(er.end(), lt.errorPath.begin(), lt.errorPath.end());
if (!lt.token) if (!lt.token)
return; return false;
if (!pred(lt.token)) if (!pred(lt.token))
return; return false;
er.emplace_back(argtok, message); er.emplace_back(argtok, message);
ValueFlow::Value value; ValueFlow::Value value;
@ -3446,22 +3484,26 @@ struct LifetimeStore {
value.setInconclusive(lt.inconclusive || inconclusive); value.setInconclusive(lt.inconclusive || inconclusive);
// Don't add the value a second time // Don't add the value a second time
if (std::find(tok->values().begin(), tok->values().end(), value) != tok->values().end()) if (std::find(tok->values().begin(), tok->values().end(), value) != tok->values().end())
return; return false;
setTokenValue(tok, value, tokenlist->getSettings()); setTokenValue(tok, value, tokenlist->getSettings());
valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings); update = true;
} }
if (update && forward)
forwardLifetime(tok, tokenlist, errorLogger, settings);
return update;
} }
void byRef(Token *tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings) const { bool byRef(Token* tok, TokenList* tokenlist, ErrorLogger* errorLogger, const Settings* settings) const
byRef(tok, tokenlist, errorLogger, settings, [](const Token *) { {
return true; return byRef(tok, tokenlist, errorLogger, settings, [](const Token*) { return true; });
});
} }
template <class Predicate> template <class Predicate>
void byVal(Token *tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings, Predicate pred) const { bool byVal(Token* tok, TokenList* tokenlist, ErrorLogger* errorLogger, const Settings* settings, Predicate pred) const
{
if (!argtok) if (!argtok)
return; return false;
bool update = false;
if (argtok->values().empty()) { if (argtok->values().empty()) {
ErrorPath er; ErrorPath er;
er.emplace_back(argtok, message); er.emplace_back(argtok, message);
@ -3476,9 +3518,9 @@ struct LifetimeStore {
value.setInconclusive(inconclusive); value.setInconclusive(inconclusive);
// Don't add the value a second time // Don't add the value a second time
if (std::find(tok->values().begin(), tok->values().end(), value) != tok->values().end()) if (std::find(tok->values().begin(), tok->values().end(), value) != tok->values().end())
return; return false;
setTokenValue(tok, value, tokenlist->getSettings()); setTokenValue(tok, value, tokenlist->getSettings());
valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings); update = true;
} }
} }
for (const ValueFlow::Value &v : argtok->values()) { for (const ValueFlow::Value &v : argtok->values()) {
@ -3491,9 +3533,9 @@ struct LifetimeStore {
ErrorPath er = v.errorPath; ErrorPath er = v.errorPath;
er.insert(er.end(), lt.errorPath.begin(), lt.errorPath.end()); er.insert(er.end(), lt.errorPath.begin(), lt.errorPath.end());
if (!lt.token) if (!lt.token)
return; return false;
if (!pred(lt.token)) if (!pred(lt.token))
return; return false;
er.emplace_back(argtok, message); er.emplace_back(argtok, message);
er.insert(er.end(), errorPath.begin(), errorPath.end()); er.insert(er.end(), errorPath.begin(), errorPath.end());
@ -3509,15 +3551,17 @@ struct LifetimeStore {
if (std::find(tok->values().begin(), tok->values().end(), value) != tok->values().end()) if (std::find(tok->values().begin(), tok->values().end(), value) != tok->values().end())
continue; continue;
setTokenValue(tok, value, tokenlist->getSettings()); setTokenValue(tok, value, tokenlist->getSettings());
valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings); update = true;
} }
} }
if (update && forward)
forwardLifetime(tok, tokenlist, errorLogger, settings);
return update;
} }
void byVal(Token *tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings) const { bool byVal(Token* tok, TokenList* tokenlist, ErrorLogger* errorLogger, const Settings* settings) const
byVal(tok, tokenlist, errorLogger, settings, [](const Token *) { {
return true; return byVal(tok, tokenlist, errorLogger, settings, [](const Token*) { return true; });
});
} }
template <class Predicate> template <class Predicate>
@ -3549,6 +3593,19 @@ struct LifetimeStore {
return true; return true;
}); });
} }
private:
Context* mContext;
void forwardLifetime(Token* tok, TokenList* tokenlist, ErrorLogger* errorLogger, const Settings* settings) const
{
if (mContext) {
mContext->tok = tok;
mContext->tokenlist = tokenlist;
mContext->errorLogger = errorLogger;
mContext->settings = settings;
}
valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings);
}
}; };
static void valueFlowLifetimeFunction(Token *tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings) static void valueFlowLifetimeFunction(Token *tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings)
@ -3603,6 +3660,7 @@ static void valueFlowLifetimeFunction(Token *tok, TokenList *tokenlist, ErrorLog
return; return;
std::vector<const Token*> returns = Function::findReturns(f); std::vector<const Token*> returns = Function::findReturns(f);
const bool inconclusive = returns.size() > 1; const bool inconclusive = returns.size() > 1;
bool update = false;
for (const Token* returnTok : returns) { for (const Token* returnTok : returns) {
if (returnTok == tok) if (returnTok == tok)
continue; continue;
@ -3610,7 +3668,8 @@ static void valueFlowLifetimeFunction(Token *tok, TokenList *tokenlist, ErrorLog
if (returnVar && returnVar->isArgument() && (returnVar->isConst() || !isVariableChanged(returnVar, settings, tokenlist->isCPP()))) { if (returnVar && returnVar->isArgument() && (returnVar->isConst() || !isVariableChanged(returnVar, settings, tokenlist->isCPP()))) {
LifetimeStore ls = LifetimeStore::fromFunctionArg(f, tok, returnVar, tokenlist, errorLogger); LifetimeStore ls = LifetimeStore::fromFunctionArg(f, tok, returnVar, tokenlist, errorLogger);
ls.inconclusive = inconclusive; ls.inconclusive = inconclusive;
ls.byVal(tok->next(), tokenlist, errorLogger, settings); ls.forward = false;
update |= ls.byVal(tok->next(), tokenlist, errorLogger, settings);
} }
for (const ValueFlow::Value &v : returnTok->values()) { for (const ValueFlow::Value &v : returnTok->values()) {
if (!v.isLifetimeValue()) if (!v.isLifetimeValue())
@ -3621,16 +3680,19 @@ static void valueFlowLifetimeFunction(Token *tok, TokenList *tokenlist, ErrorLog
LifetimeStore ls = LifetimeStore::fromFunctionArg(f, tok, var, tokenlist, errorLogger); LifetimeStore ls = LifetimeStore::fromFunctionArg(f, tok, var, tokenlist, errorLogger);
if (!ls.argtok) if (!ls.argtok)
continue; continue;
ls.forward = false;
ls.inconclusive = inconclusive; ls.inconclusive = inconclusive;
ls.errorPath = v.errorPath; ls.errorPath = v.errorPath;
ls.errorPath.emplace_front(returnTok, "Return " + lifetimeType(returnTok, &v) + "."); ls.errorPath.emplace_front(returnTok, "Return " + lifetimeType(returnTok, &v) + ".");
if (!v.isArgumentLifetimeValue() && (var->isReference() || var->isRValueReference())) { if (!v.isArgumentLifetimeValue() && (var->isReference() || var->isRValueReference())) {
ls.byRef(tok->next(), tokenlist, errorLogger, settings); update |= ls.byRef(tok->next(), tokenlist, errorLogger, settings);
} else if (v.isArgumentLifetimeValue()) { } else if (v.isArgumentLifetimeValue()) {
ls.byVal(tok->next(), tokenlist, errorLogger, settings); update |= ls.byVal(tok->next(), tokenlist, errorLogger, settings);
} }
} }
} }
if (update)
valueFlowForwardLifetime(tok->next(), tokenlist, errorLogger, settings);
} }
} }
@ -3648,11 +3710,11 @@ static void valueFlowLifetimeConstructor(Token* tok,
// If the type is unknown then assume it captures by value in the // If the type is unknown then assume it captures by value in the
// constructor, but make each lifetime inconclusive // constructor, but make each lifetime inconclusive
std::vector<const Token*> args = getArguments(tok); std::vector<const Token*> args = getArguments(tok);
for (const Token *argtok : args) { LifetimeStore::forEach(
LifetimeStore ls{argtok, "Passed to initializer list.", ValueFlow::Value::LifetimeKind::Object}; args, "Passed to initializer list.", ValueFlow::Value::LifetimeKind::Object, [&](LifetimeStore& ls) {
ls.inconclusive = true; ls.inconclusive = true;
ls.byVal(tok, tokenlist, errorLogger, settings); ls.byVal(tok, tokenlist, errorLogger, settings);
} });
return; return;
} }
const Scope* scope = t->classScope; const Scope* scope = t->classScope;
@ -3661,20 +3723,21 @@ static void valueFlowLifetimeConstructor(Token* tok,
// Only support aggregate constructors for now // Only support aggregate constructors for now
if (scope->numConstructors == 0 && t->derivedFrom.empty() && (t->isClassType() || t->isStructType())) { if (scope->numConstructors == 0 && t->derivedFrom.empty() && (t->isClassType() || t->isStructType())) {
std::vector<const Token*> args = getArguments(tok); std::vector<const Token*> args = getArguments(tok);
std::size_t i = 0; auto it = scope->varlist.begin();
for (const Variable& var : scope->varlist) { LifetimeStore::forEach(args,
if (i >= args.size()) "Passed to constructor of '" + t->name() + "'.",
break; ValueFlow::Value::LifetimeKind::Object,
const Token* argtok = args[i]; [&](const LifetimeStore& ls) {
LifetimeStore ls{ if (it == scope->varlist.end())
argtok, "Passed to constructor of '" + t->name() + "'.", ValueFlow::Value::LifetimeKind::Object}; return;
if (var.isReference() || var.isRValueReference()) { const Variable& var = *it;
ls.byRef(tok, tokenlist, errorLogger, settings); if (var.isReference() || var.isRValueReference()) {
} else { ls.byRef(tok, tokenlist, errorLogger, settings);
ls.byVal(tok, tokenlist, errorLogger, settings); } else {
} ls.byVal(tok, tokenlist, errorLogger, settings);
i++; }
} it++;
});
} }
} }
@ -3704,15 +3767,15 @@ static void valueFlowLifetimeConstructor(Token* tok, TokenList* tokenlist, Error
std::vector<const Token *> args = getArguments(tok); std::vector<const Token *> args = getArguments(tok);
// Assume range constructor if passed a pair of iterators // Assume range constructor if passed a pair of iterators
if (astIsContainer(parent) && args.size() == 2 && astIsIterator(args[0]) && astIsIterator(args[1])) { if (astIsContainer(parent) && args.size() == 2 && astIsIterator(args[0]) && astIsIterator(args[1])) {
for (const Token *argtok : args) { LifetimeStore::forEach(
LifetimeStore ls{argtok, "Passed to initializer list.", ValueFlow::Value::LifetimeKind::Object}; args, "Passed to initializer list.", ValueFlow::Value::LifetimeKind::Object, [&](const LifetimeStore& ls) {
ls.byDerefCopy(tok, tokenlist, errorLogger, settings); ls.byDerefCopy(tok, tokenlist, errorLogger, settings);
} });
} else { } else {
for (const Token *argtok : args) { LifetimeStore::forEach(args,
LifetimeStore ls{argtok, "Passed to initializer list.", ValueFlow::Value::LifetimeKind::Object}; "Passed to initializer list.",
ls.byVal(tok, tokenlist, errorLogger, settings); ValueFlow::Value::LifetimeKind::Object,
} [&](const LifetimeStore& ls) { ls.byVal(tok, tokenlist, errorLogger, settings); });
} }
} else { } else {
valueFlowLifetimeConstructor(tok, Token::typeOf(tok->previous()), tokenlist, errorLogger, settings); valueFlowLifetimeConstructor(tok, Token::typeOf(tok->previous()), tokenlist, errorLogger, settings);
@ -3822,16 +3885,21 @@ static void valueFlowLifetime(TokenList *tokenlist, SymbolDatabase*, ErrorLogger
return true; return true;
}; };
bool update = false;
auto captureVariable = [&](const Token* tok2, Lambda::Capture c, std::function<bool(const Token*)> pred) { auto captureVariable = [&](const Token* tok2, Lambda::Capture c, std::function<bool(const Token*)> pred) {
if (varids.count(tok->varId()) > 0) if (varids.count(tok->varId()) > 0)
return; return;
ErrorPath errorPath; ErrorPath errorPath;
if (c == Lambda::Capture::ByReference) { if (c == Lambda::Capture::ByReference) {
LifetimeStore{tok2, "Lambda captures variable by reference here.", ValueFlow::Value::LifetimeKind::Lambda} .byRef( LifetimeStore ls{
tok, tokenlist, errorLogger, settings, pred); tok2, "Lambda captures variable by reference here.", ValueFlow::Value::LifetimeKind::Lambda};
ls.forward = false;
update |= ls.byRef(tok, tokenlist, errorLogger, settings, pred);
} else if (c == Lambda::Capture::ByValue) { } else if (c == Lambda::Capture::ByValue) {
LifetimeStore{tok2, "Lambda captures variable by value here.", ValueFlow::Value::LifetimeKind::Lambda} .byVal( LifetimeStore ls{
tok, tokenlist, errorLogger, settings, pred); tok2, "Lambda captures variable by value here.", ValueFlow::Value::LifetimeKind::Lambda};
ls.forward = false;
update |= ls.byVal(tok, tokenlist, errorLogger, settings, pred);
} }
}; };
@ -3853,8 +3921,8 @@ static void valueFlowLifetime(TokenList *tokenlist, SymbolDatabase*, ErrorLogger
continue; continue;
captureVariable(tok2, lam.implicitCapture, isImplicitCapturingVariable); captureVariable(tok2, lam.implicitCapture, isImplicitCapturingVariable);
} }
if (update)
valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings); valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings);
} }
// address of // address of
else if (tok->isUnaryOp("&")) { else if (tok->isUnaryOp("&")) {

View File

@ -4897,6 +4897,36 @@ private:
" ) {}\n" " ) {}\n"
"}\n"; "}\n";
valueOfTok(code, "x"); valueOfTok(code, "x");
code = "namespace {\n"
"struct a {\n"
" a(...) {}\n"
" a(std::initializer_list<std::pair<int, std::vector<std::vector<a>>>>) {}\n"
"} b{{0, {{&b, &b, &b, &b}}},\n"
" {0,\n"
" {{&b, &b, &b, &b, &b, &b, &b, &b, &b, &b},\n"
" {{&b, &b, &b, &b, &b, &b, &b}}}},\n"
" {0,\n"
" {{&b, &b, &b, &b, &b, &b, &b, &b, &b, &b, &b, &b, &b, &b},\n"
" {&b, &b, &b, &b, &b, &b, &b, &b, &b, &b, &b}}}};\n"
"}\n";
valueOfTok(code, "x");
code = "namespace {\n"
"struct a {\n"
" a(...) {}\n"
" a(std::initializer_list<std::pair<int, std::vector<std::vector<a>>>>) {}\n"
"} b{{0, {{&b}}},\n"
" {0, {{&b}}},\n"
" {0, {{&b}}},\n"
" {0, {{&b}}},\n"
" {0, {{&b}, {&b, &b, &b, &b, &b, &b, &b, &b, &b, &b, {&b}}}},\n"
" {0,\n"
" {{&b},\n"
" {&b, &b, &b, &b, &b, &b, &b, &b, &b, &b, &b, &b, &b, &b, &b, &b, &b, &b,\n"
" &b}}}};\n"
"}\n";
valueOfTok(code, "x");
} }
void valueFlowCrashConstructorInitialization() { // #9577 void valueFlowCrashConstructorInitialization() { // #9577