Extend lifetimes to subfunctions

This commit is contained in:
Paul 2020-09-04 11:56:34 -05:00
parent efa113db55
commit bb37b07def
4 changed files with 45 additions and 20 deletions

View File

@ -511,28 +511,31 @@ void CheckAutoVariables::checkVarLifetimeScope(const Token * start, const Token
} }
} }
for (const ValueFlow::Value& val:tok->values()) { for (const ValueFlow::Value& val:tok->values()) {
if (!val.isLocalLifetimeValue()) if (!val.isLocalLifetimeValue() && !val.isSubFunctionLifetimeValue())
continue; continue;
for (const LifetimeToken& lt :getLifetimeTokens(getParentLifetime(val.tokvalue))) { for (const LifetimeToken& lt :getLifetimeTokens(getParentLifetime(val.tokvalue))) {
const Token * tokvalue = lt.token; const Token * tokvalue = lt.token;
if (Token::Match(tok->astParent(), "return|throw")) { if (val.isLocalLifetimeValue()) {
if (getPointerDepth(tok) < getPointerDepth(tokvalue)) if (Token::Match(tok->astParent(), "return|throw")) {
continue; if (getPointerDepth(tok) < getPointerDepth(tokvalue))
if (!isLifetimeBorrowed(tok, mSettings)) continue;
continue; if (!isLifetimeBorrowed(tok, mSettings))
if ((tokvalue->variable() && !isEscapedReference(tokvalue->variable()) && continue;
isInScope(tokvalue->variable()->nameToken(), scope)) || if ((tokvalue->variable() && !isEscapedReference(tokvalue->variable()) &&
isDeadTemporary(mTokenizer->isCPP(), tokvalue, tok, &mSettings->library)) { isInScope(tokvalue->variable()->nameToken(), scope)) ||
errorReturnDanglingLifetime(tok, &val); isDeadTemporary(mTokenizer->isCPP(), tokvalue, tok, &mSettings->library)) {
errorReturnDanglingLifetime(tok, &val);
break;
}
} else if (tokvalue->variable() && isDeadScope(tokvalue->variable()->nameToken(), tok->scope())) {
errorInvalidLifetime(tok, &val);
break;
} else if (!tokvalue->variable() && isDeadTemporary(mTokenizer->isCPP(), tokvalue, tok, &mSettings->library)) {
errorDanglingTemporaryLifetime(tok, &val);
break; break;
} }
} else if (tokvalue->variable() && isDeadScope(tokvalue->variable()->nameToken(), tok->scope())) { }
errorInvalidLifetime(tok, &val); if (tokvalue->variable() && (isInScope(tokvalue->variable()->nameToken(), tok->scope()) || (val.isSubFunctionLifetimeValue() && tokvalue->variable()->isLocal()))) {
break;
} else if (!tokvalue->variable() && isDeadTemporary(mTokenizer->isCPP(), tokvalue, tok, &mSettings->library)) {
errorDanglingTemporaryLifetime(tok, &val);
break;
} else if (tokvalue->variable() && isInScope(tokvalue->variable()->nameToken(), tok->scope())) {
const Variable * var = nullptr; const Variable * var = nullptr;
const Token * tok2 = tok; const Token * tok2 = tok;
if (Token::simpleMatch(tok->astParent(), "=")) { if (Token::simpleMatch(tok->astParent(), "=")) {

View File

@ -3874,6 +3874,10 @@ static void valueFlowLifetime(TokenList *tokenlist, SymbolDatabase*, ErrorLogger
valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings); valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings);
} }
} }
// Forward any lifetimes
else if (std::any_of(tok->values().begin(), tok->values().end(), std::mem_fn(&ValueFlow::Value::isLifetimeValue))) {
valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings);
}
} }
} }
@ -5437,8 +5441,6 @@ static void valueFlowSubFunction(TokenList* tokenlist, SymbolDatabase* symboldat
// passing value(s) to function // passing value(s) to function
std::list<ValueFlow::Value> argvalues(getFunctionArgumentValues(argtok)); std::list<ValueFlow::Value> argvalues(getFunctionArgumentValues(argtok));
// Don't forward lifetime values
argvalues.remove_if(std::mem_fn(&ValueFlow::Value::isLifetimeValue));
// Don't forward container sizes for now since programmemory can't evaluate conditions // Don't forward container sizes for now since programmemory can't evaluate conditions
argvalues.remove_if(std::mem_fn(&ValueFlow::Value::isContainerSizeValue)); argvalues.remove_if(std::mem_fn(&ValueFlow::Value::isContainerSizeValue));
@ -5459,6 +5461,9 @@ static void valueFlowSubFunction(TokenList* tokenlist, SymbolDatabase* symboldat
"' value is " + "' value is " +
v.infoString()); v.infoString());
v.path = 256 * v.path + id; v.path = 256 * v.path + id;
// Change scope of lifetime values
if (v.isLifetimeValue())
v.lifetimeScope = ValueFlow::Value::LifetimeScope::SubFunction;
} }
// passed values are not "known".. // passed values are not "known"..
@ -6649,6 +6654,7 @@ void ValueFlow::setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase,
valueFlowSwitchVariable(tokenlist, symboldatabase, errorLogger, settings); valueFlowSwitchVariable(tokenlist, symboldatabase, errorLogger, settings);
valueFlowForLoop(tokenlist, symboldatabase, errorLogger, settings); valueFlowForLoop(tokenlist, symboldatabase, errorLogger, settings);
valueFlowSubFunction(tokenlist, symboldatabase, errorLogger, settings); valueFlowSubFunction(tokenlist, symboldatabase, errorLogger, settings);
valueFlowLifetime(tokenlist, symboldatabase, errorLogger, settings);
valueFlowFunctionDefaultParameter(tokenlist, symboldatabase, errorLogger, settings); valueFlowFunctionDefaultParameter(tokenlist, symboldatabase, errorLogger, settings);
valueFlowUninit(tokenlist, symboldatabase, errorLogger, settings); valueFlowUninit(tokenlist, symboldatabase, errorLogger, settings);
if (tokenlist->isCPP()) { if (tokenlist->isCPP()) {

View File

@ -217,6 +217,10 @@ namespace ValueFlow {
return valueType == ValueType::LIFETIME && lifetimeScope == LifetimeScope::Argument; return valueType == ValueType::LIFETIME && lifetimeScope == LifetimeScope::Argument;
} }
bool isSubFunctionLifetimeValue() const {
return valueType == ValueType::LIFETIME && lifetimeScope == LifetimeScope::SubFunction;
}
bool isNonValue() const { bool isNonValue() const {
return isMovedValue() || isUninitValue() || isLifetimeValue(); return isMovedValue() || isUninitValue() || isLifetimeValue();
} }
@ -263,7 +267,7 @@ namespace ValueFlow {
enum class LifetimeKind {Object, SubObject, Lambda, Iterator, Address} lifetimeKind; enum class LifetimeKind {Object, SubObject, Lambda, Iterator, Address} lifetimeKind;
enum class LifetimeScope { Local, Argument } lifetimeScope; enum class LifetimeScope { Local, Argument, SubFunction } lifetimeScope;
static const char* toString(MoveKind moveKind); static const char* toString(MoveKind moveKind);

View File

@ -2156,6 +2156,18 @@ private:
" std::sort(x.begin(), x.end());\n" " std::sort(x.begin(), x.end());\n"
"}\n"); "}\n");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
check("struct A {\n"
" std::vector<int*> v;\n"
" void add(int* i) {\n"
" v.push_back(i);\n"
" }\n"
" void f() {\n"
" int i = 0;\n"
" add(&i);\n"
" }\n"
"};\n");
ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:8] -> [test.cpp:4] -> [test.cpp:7] -> [test.cpp:4]: (error) Non-local variable 'v' will use object that points to local variable 'i'.\n", errout.str());
} }
void danglingLifetime() { void danglingLifetime() {