New Check: Detect return of invalid reference
This commit is contained in:
parent
eda60f6483
commit
f62466493b
|
@ -306,3 +306,86 @@ void CheckAutoVariables::errorAutoVariableAssignment(const Token *tok)
|
|||
reportError(tok, Severity::error, "autoVariables", "Wrong assignment of an auto-variable to an effective parameter of a function");
|
||||
}
|
||||
|
||||
|
||||
void CheckAutoVariables::returnReference()
|
||||
{
|
||||
// locate function that returns a reference..
|
||||
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next())
|
||||
{
|
||||
// Skip executable scopes..
|
||||
if (Token::Match(tok, ") const| {"))
|
||||
{
|
||||
tok = tok->next();
|
||||
if (tok->str() == "const")
|
||||
tok = tok->next();
|
||||
tok = tok->link();
|
||||
continue;
|
||||
}
|
||||
|
||||
// have we reached a function that returns a reference?
|
||||
if (Token::Match(tok, "%type% & %var% (") ||
|
||||
Token::Match(tok, "> & %var% ("))
|
||||
{
|
||||
// go to the '('
|
||||
const Token *tok2 = tok->tokAt(3);
|
||||
|
||||
// go to the ')'
|
||||
tok2 = tok2->link();
|
||||
|
||||
// is this a function implementation?
|
||||
if (Token::Match(tok2, ") const| {"))
|
||||
{
|
||||
unsigned int indentlevel = 0;
|
||||
std::set<unsigned int> localvar; // local variables in function
|
||||
while (0 != (tok2 = tok2->next()))
|
||||
{
|
||||
// indentlevel..
|
||||
if (tok2->str() == "{")
|
||||
++indentlevel;
|
||||
else if (tok2->str() == "}")
|
||||
{
|
||||
if (indentlevel <= 1)
|
||||
break;
|
||||
--indentlevel;
|
||||
}
|
||||
|
||||
// declare local variable..
|
||||
if (Token::Match(tok2, "[{};] %type%") && tok2->next()->str() != "return")
|
||||
{
|
||||
// goto next token..
|
||||
tok2 = tok2->next();
|
||||
|
||||
// skip "std::" if it is seen
|
||||
if (Token::simpleMatch(tok2, "std ::"))
|
||||
tok2 = tok2->tokAt(2);
|
||||
|
||||
// is it a variable declaration?
|
||||
if (Token::Match(tok2, "%type% %var% ;"))
|
||||
localvar.insert(tok2->next()->varId());
|
||||
else if (Token::Match(tok2, "%type% < %any% > %var% ;"))
|
||||
localvar.insert(tok2->tokAt(4)->varId());
|
||||
}
|
||||
|
||||
// return..
|
||||
else if (Token::Match(tok2, "return %var% ;"))
|
||||
{
|
||||
// is the returned variable a local variable?
|
||||
if ((tok2->next()->varId() > 0) &&
|
||||
(localvar.find(tok2->next()->varId()) != localvar.end()))
|
||||
{
|
||||
// report error..
|
||||
errorReturnReference(tok2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CheckAutoVariables::errorReturnReference(const Token *tok)
|
||||
{
|
||||
reportError(tok, Severity::error, "returnReference", "Returning reference to auto variable");
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -47,6 +47,7 @@ public:
|
|||
CheckAutoVariables checkAutoVariables(tokenizer, settings, errorLogger);
|
||||
checkAutoVariables.autoVariables();
|
||||
checkAutoVariables.returnPointerToLocalArray();
|
||||
checkAutoVariables.returnReference();
|
||||
}
|
||||
|
||||
/** Check auto variables */
|
||||
|
@ -55,6 +56,9 @@ public:
|
|||
/** Returning pointer to local array */
|
||||
void returnPointerToLocalArray();
|
||||
|
||||
/** Returning reference to local/temporary variable */
|
||||
void returnReference();
|
||||
|
||||
private:
|
||||
std::set<std::string> fp_list;
|
||||
std::set<unsigned int> vd_list;
|
||||
|
@ -67,15 +71,15 @@ private:
|
|||
|
||||
|
||||
|
||||
|
||||
void errorReturnPointerToLocalArray(const Token *tok);
|
||||
void errorAutoVariableAssignment(const Token *tok);
|
||||
|
||||
void errorReturnReference(const Token *tok);
|
||||
|
||||
void getErrorMessages()
|
||||
{
|
||||
errorAutoVariableAssignment(0);
|
||||
errorReturnPointerToLocalArray(0);
|
||||
errorReturnReference(0);
|
||||
}
|
||||
|
||||
std::string name() const
|
||||
|
@ -88,7 +92,8 @@ private:
|
|||
return "A pointer to a variable is only valid as long as the variable is in scope.\n"
|
||||
"Check:\n"
|
||||
"* returning a pointer to variable\n"
|
||||
"* assigning address of an variable to an effective parameter of a function\n";
|
||||
"* assigning address of an variable to an effective parameter of a function\n"
|
||||
"* returning reference to local/temporary variable\n";
|
||||
}
|
||||
};
|
||||
/// @}
|
||||
|
|
|
@ -57,6 +57,7 @@ private:
|
|||
CheckAutoVariables checkAutoVariables(&tokenizer, &settings, this);
|
||||
checkAutoVariables.autoVariables();
|
||||
checkAutoVariables.returnPointerToLocalArray();
|
||||
checkAutoVariables.returnReference();
|
||||
}
|
||||
|
||||
void run()
|
||||
|
@ -69,6 +70,9 @@ private:
|
|||
|
||||
TEST_CASE(returnLocalVariable1);
|
||||
TEST_CASE(returnLocalVariable2);
|
||||
|
||||
// return reference..
|
||||
TEST_CASE(returnReference);
|
||||
}
|
||||
|
||||
|
||||
|
@ -95,7 +99,8 @@ private:
|
|||
check("void func1(int* arr[2])\n"
|
||||
"{\n"
|
||||
" int num=2;"
|
||||
"arr[0]=#}");
|
||||
" arr[0]=#\n"
|
||||
"}");
|
||||
ASSERT_EQUALS("[test.cpp:3]: (error) Wrong assignment of an auto-variable to an effective parameter of a function\n", errout.str());
|
||||
}
|
||||
|
||||
|
@ -159,6 +164,31 @@ private:
|
|||
"}\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
void returnReference()
|
||||
{
|
||||
check("std::string &foo()\n"
|
||||
"{\n"
|
||||
" std::string s;\n"
|
||||
" return s;\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS("[test.cpp:4]: (error) Returning reference to auto variable\n", errout.str());
|
||||
|
||||
check("std::vector<int> &foo()\n"
|
||||
"{\n"
|
||||
" std::vector<int> v;\n"
|
||||
" return v;\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS("[test.cpp:4]: (error) Returning reference to auto variable\n", errout.str());
|
||||
|
||||
check("std::vector<int> &foo()\n"
|
||||
"{\n"
|
||||
" static std::vector<int> v;\n"
|
||||
" return v;\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_TEST(TestAutoVariables)
|
||||
|
|
Loading…
Reference in New Issue