diff --git a/lib/checkautovariables.cpp b/lib/checkautovariables.cpp index f36694b32..d67a02b14 100644 --- a/lib/checkautovariables.cpp +++ b/lib/checkautovariables.cpp @@ -511,28 +511,31 @@ void CheckAutoVariables::checkVarLifetimeScope(const Token * start, const Token } } for (const ValueFlow::Value& val:tok->values()) { - if (!val.isLocalLifetimeValue()) + if (!val.isLocalLifetimeValue() && !val.isSubFunctionLifetimeValue()) continue; for (const LifetimeToken& lt :getLifetimeTokens(getParentLifetime(val.tokvalue))) { const Token * tokvalue = lt.token; - if (Token::Match(tok->astParent(), "return|throw")) { - if (getPointerDepth(tok) < getPointerDepth(tokvalue)) - continue; - if (!isLifetimeBorrowed(tok, mSettings)) - continue; - if ((tokvalue->variable() && !isEscapedReference(tokvalue->variable()) && - isInScope(tokvalue->variable()->nameToken(), scope)) || - isDeadTemporary(mTokenizer->isCPP(), tokvalue, tok, &mSettings->library)) { - errorReturnDanglingLifetime(tok, &val); + if (val.isLocalLifetimeValue()) { + if (Token::Match(tok->astParent(), "return|throw")) { + if (getPointerDepth(tok) < getPointerDepth(tokvalue)) + continue; + if (!isLifetimeBorrowed(tok, mSettings)) + continue; + if ((tokvalue->variable() && !isEscapedReference(tokvalue->variable()) && + isInScope(tokvalue->variable()->nameToken(), scope)) || + 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; } - } 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; - } else if (tokvalue->variable() && isInScope(tokvalue->variable()->nameToken(), tok->scope())) { + } + if (tokvalue->variable() && (isInScope(tokvalue->variable()->nameToken(), tok->scope()) || (val.isSubFunctionLifetimeValue() && tokvalue->variable()->isLocal()))) { const Variable * var = nullptr; const Token * tok2 = tok; if (Token::simpleMatch(tok->astParent(), "=")) { diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index 746ed1f35..758f4ea38 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -3874,6 +3874,10 @@ static void valueFlowLifetime(TokenList *tokenlist, SymbolDatabase*, ErrorLogger 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 std::list 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 argvalues.remove_if(std::mem_fn(&ValueFlow::Value::isContainerSizeValue)); @@ -5459,6 +5461,9 @@ static void valueFlowSubFunction(TokenList* tokenlist, SymbolDatabase* symboldat "' value is " + v.infoString()); 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".. @@ -6649,6 +6654,7 @@ void ValueFlow::setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase, valueFlowSwitchVariable(tokenlist, symboldatabase, errorLogger, settings); valueFlowForLoop(tokenlist, symboldatabase, errorLogger, settings); valueFlowSubFunction(tokenlist, symboldatabase, errorLogger, settings); + valueFlowLifetime(tokenlist, symboldatabase, errorLogger, settings); valueFlowFunctionDefaultParameter(tokenlist, symboldatabase, errorLogger, settings); valueFlowUninit(tokenlist, symboldatabase, errorLogger, settings); if (tokenlist->isCPP()) { diff --git a/lib/valueflow.h b/lib/valueflow.h index 669d00c27..646f1aa2a 100644 --- a/lib/valueflow.h +++ b/lib/valueflow.h @@ -217,6 +217,10 @@ namespace ValueFlow { return valueType == ValueType::LIFETIME && lifetimeScope == LifetimeScope::Argument; } + bool isSubFunctionLifetimeValue() const { + return valueType == ValueType::LIFETIME && lifetimeScope == LifetimeScope::SubFunction; + } + bool isNonValue() const { return isMovedValue() || isUninitValue() || isLifetimeValue(); } @@ -263,7 +267,7 @@ namespace ValueFlow { 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); diff --git a/test/testautovariables.cpp b/test/testautovariables.cpp index 059d15064..b188d9e74 100644 --- a/test/testautovariables.cpp +++ b/test/testautovariables.cpp @@ -2156,6 +2156,18 @@ private: " std::sort(x.begin(), x.end());\n" "}\n"); ASSERT_EQUALS("", errout.str()); + + check("struct A {\n" + " std::vector 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() {