Fix issue 8899: False positive returnDanglingLifetime when returning by value
This fixes the FP from: ```cpp #include <string> class MyString { public: MyString(char* source) { length = strlen( source ); buffer = new char[length+1]; if( buffer ) { strcpy( buffer, source ); } } char* buffer; int length; }; MyString Foo() { char arr[20]; sprintf(arr, "hello world"); return arr; } void main() { MyString str = Foo(); printf(str.buffer); } ```
This commit is contained in:
parent
1bfe98447a
commit
45dcfad9f9
|
@ -2743,6 +2743,27 @@ struct Lambda {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static bool isDecayedPointer(const Token *tok, const Settings *settings)
|
||||||
|
{
|
||||||
|
if (!tok)
|
||||||
|
return false;
|
||||||
|
if (astIsPointer(tok->astParent()) && !Token::simpleMatch(tok->astParent(), "return"))
|
||||||
|
return true;
|
||||||
|
if (!Token::simpleMatch(tok->astParent(), "return"))
|
||||||
|
return false;
|
||||||
|
if (!tok->scope())
|
||||||
|
return false;
|
||||||
|
if (!tok->scope()->function)
|
||||||
|
return false;
|
||||||
|
if (!tok->scope()->function->retDef)
|
||||||
|
return false;
|
||||||
|
// TODO: Add valuetypes to return types of functions
|
||||||
|
ValueType vt = ValueType::parseDecl(tok->scope()->function->retDef, settings);
|
||||||
|
if (vt.pointer > 0)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static void valueFlowLifetime(TokenList *tokenlist, SymbolDatabase*, ErrorLogger *errorLogger, const Settings *settings)
|
static void valueFlowLifetime(TokenList *tokenlist, SymbolDatabase*, ErrorLogger *errorLogger, const Settings *settings)
|
||||||
{
|
{
|
||||||
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
|
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
|
||||||
|
@ -2846,8 +2867,9 @@ static void valueFlowLifetime(TokenList *tokenlist, SymbolDatabase*, ErrorLogger
|
||||||
const Variable * var = getLifetimeVariable(tok, errorPath);
|
const Variable * var = getLifetimeVariable(tok, errorPath);
|
||||||
if (!var)
|
if (!var)
|
||||||
continue;
|
continue;
|
||||||
if (var->isArray() && !var->isStlType() && !var->isArgument() && tok->astParent() &&
|
if (var->nameToken() == tok)
|
||||||
(astIsPointer(tok->astParent()) || Token::Match(tok->astParent(), "%assign%|return"))) {
|
continue;
|
||||||
|
if (var->isArray() && !var->isStlType() && !var->isArgument() && isDecayedPointer(tok, settings)) {
|
||||||
errorPath.emplace_back(tok, "Array decayed to pointer here.");
|
errorPath.emplace_back(tok, "Array decayed to pointer here.");
|
||||||
|
|
||||||
ValueFlow::Value value;
|
ValueFlow::Value value;
|
||||||
|
|
|
@ -717,7 +717,7 @@ private:
|
||||||
" return str;\n"
|
" return str;\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS(
|
ASSERT_EQUALS(
|
||||||
"[test.cpp:3] -> [test.cpp:3] -> [test.cpp:4]: (error) Returning pointer to local variable 'str' that will be invalid when returning.\n",
|
"[test.cpp:4] -> [test.cpp:3] -> [test.cpp:4]: (error) Returning pointer to local variable 'str' that will be invalid when returning.\n",
|
||||||
errout.str());
|
errout.str());
|
||||||
|
|
||||||
check("char *foo()\n" // use ValueFlow
|
check("char *foo()\n" // use ValueFlow
|
||||||
|
@ -727,7 +727,7 @@ private:
|
||||||
" return p;\n"
|
" return p;\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS(
|
ASSERT_EQUALS(
|
||||||
"[test.cpp:3] -> [test.cpp:3] -> [test.cpp:5]: (error) Returning pointer to local variable 'str' that will be invalid when returning.\n",
|
"[test.cpp:4] -> [test.cpp:3] -> [test.cpp:5]: (error) Returning pointer to local variable 'str' that will be invalid when returning.\n",
|
||||||
errout.str());
|
errout.str());
|
||||||
|
|
||||||
check("class Fred {\n"
|
check("class Fred {\n"
|
||||||
|
@ -739,7 +739,7 @@ private:
|
||||||
" return str;\n"
|
" return str;\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS(
|
ASSERT_EQUALS(
|
||||||
"[test.cpp:6] -> [test.cpp:6] -> [test.cpp:7]: (error) Returning pointer to local variable 'str' that will be invalid when returning.\n",
|
"[test.cpp:7] -> [test.cpp:6] -> [test.cpp:7]: (error) Returning pointer to local variable 'str' that will be invalid when returning.\n",
|
||||||
errout.str());
|
errout.str());
|
||||||
|
|
||||||
check("char * format_reg(char *outbuffer_start) {\n"
|
check("char * format_reg(char *outbuffer_start) {\n"
|
||||||
|
@ -795,7 +795,7 @@ private:
|
||||||
" return x+5;\n"
|
" return x+5;\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS(
|
ASSERT_EQUALS(
|
||||||
"[test.cpp:2] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning pointer to local variable 'x' that will be invalid when returning.\n",
|
"[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning pointer to local variable 'x' that will be invalid when returning.\n",
|
||||||
errout.str());
|
errout.str());
|
||||||
|
|
||||||
check("char *foo(int y) {\n"
|
check("char *foo(int y) {\n"
|
||||||
|
@ -803,7 +803,7 @@ private:
|
||||||
" return (x+8)-y;\n"
|
" return (x+8)-y;\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS(
|
ASSERT_EQUALS(
|
||||||
"[test.cpp:2] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning pointer to local variable 'x' that will be invalid when returning.\n",
|
"[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning pointer to local variable 'x' that will be invalid when returning.\n",
|
||||||
errout.str());
|
errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -813,7 +813,7 @@ private:
|
||||||
" return (char *)x;\n"
|
" return (char *)x;\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS(
|
ASSERT_EQUALS(
|
||||||
"[test.cpp:2] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning pointer to local variable 'x' that will be invalid when returning.\n",
|
"[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning pointer to local variable 'x' that will be invalid when returning.\n",
|
||||||
errout.str());
|
errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1533,6 +1533,15 @@ private:
|
||||||
"}\n");
|
"}\n");
|
||||||
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3] -> [test.cpp:1] -> [test.cpp:3]: (error) Returning lambda that captures local variable 'a' that will be invalid when returning.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3] -> [test.cpp:1] -> [test.cpp:3]: (error) Returning lambda that captures local variable 'a' that will be invalid when returning.\n", errout.str());
|
||||||
|
|
||||||
|
check("struct e {};\n"
|
||||||
|
"e * j() {\n"
|
||||||
|
" e c[20];\n"
|
||||||
|
" return c;\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS(
|
||||||
|
"[test.cpp:4] -> [test.cpp:3] -> [test.cpp:4]: (error) Returning pointer to local variable 'c' that will be invalid when returning.\n",
|
||||||
|
errout.str());
|
||||||
|
|
||||||
check("auto f(std::vector<int>& a) {\n"
|
check("auto f(std::vector<int>& a) {\n"
|
||||||
" auto it = a.begin();\n"
|
" auto it = a.begin();\n"
|
||||||
" return [=](){ return it; };\n"
|
" return [=](){ return it; };\n"
|
||||||
|
@ -1605,6 +1614,25 @@ private:
|
||||||
" }\n"
|
" }\n"
|
||||||
"};\n");
|
"};\n");
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("struct a {\n"
|
||||||
|
" a(char* b) {}\n"
|
||||||
|
"};\n"
|
||||||
|
"a f() {\n"
|
||||||
|
" char c[20];\n"
|
||||||
|
" return c;\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("struct a {\n"
|
||||||
|
" a(char* b) {}\n"
|
||||||
|
"};\n"
|
||||||
|
"a g() {\n"
|
||||||
|
" char c[20];\n"
|
||||||
|
" a d = c;\n"
|
||||||
|
" return d;\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void danglingLifetimeFunction() {
|
void danglingLifetimeFunction() {
|
||||||
|
@ -1652,7 +1680,7 @@ private:
|
||||||
" x[3];\n"
|
" x[3];\n"
|
||||||
"}\n");
|
"}\n");
|
||||||
ASSERT_EQUALS(
|
ASSERT_EQUALS(
|
||||||
"[test.cpp:4] -> [test.cpp:4] -> [test.cpp:7]: (error) Using pointer to local variable 'y' that is out of scope.\n",
|
"[test.cpp:5] -> [test.cpp:4] -> [test.cpp:7]: (error) Using pointer to local variable 'y' that is out of scope.\n",
|
||||||
errout.str());
|
errout.str());
|
||||||
|
|
||||||
check("void foo(int a) {\n"
|
check("void foo(int a) {\n"
|
||||||
|
|
Loading…
Reference in New Issue