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");
|
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 checkAutoVariables(tokenizer, settings, errorLogger);
|
||||||
checkAutoVariables.autoVariables();
|
checkAutoVariables.autoVariables();
|
||||||
checkAutoVariables.returnPointerToLocalArray();
|
checkAutoVariables.returnPointerToLocalArray();
|
||||||
|
checkAutoVariables.returnReference();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Check auto variables */
|
/** Check auto variables */
|
||||||
|
@ -55,6 +56,9 @@ public:
|
||||||
/** Returning pointer to local array */
|
/** Returning pointer to local array */
|
||||||
void returnPointerToLocalArray();
|
void returnPointerToLocalArray();
|
||||||
|
|
||||||
|
/** Returning reference to local/temporary variable */
|
||||||
|
void returnReference();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::set<std::string> fp_list;
|
std::set<std::string> fp_list;
|
||||||
std::set<unsigned int> vd_list;
|
std::set<unsigned int> vd_list;
|
||||||
|
@ -67,15 +71,15 @@ private:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void errorReturnPointerToLocalArray(const Token *tok);
|
void errorReturnPointerToLocalArray(const Token *tok);
|
||||||
void errorAutoVariableAssignment(const Token *tok);
|
void errorAutoVariableAssignment(const Token *tok);
|
||||||
|
void errorReturnReference(const Token *tok);
|
||||||
|
|
||||||
void getErrorMessages()
|
void getErrorMessages()
|
||||||
{
|
{
|
||||||
errorAutoVariableAssignment(0);
|
errorAutoVariableAssignment(0);
|
||||||
errorReturnPointerToLocalArray(0);
|
errorReturnPointerToLocalArray(0);
|
||||||
|
errorReturnReference(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string name() const
|
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"
|
return "A pointer to a variable is only valid as long as the variable is in scope.\n"
|
||||||
"Check:\n"
|
"Check:\n"
|
||||||
"* returning a pointer to variable\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 checkAutoVariables(&tokenizer, &settings, this);
|
||||||
checkAutoVariables.autoVariables();
|
checkAutoVariables.autoVariables();
|
||||||
checkAutoVariables.returnPointerToLocalArray();
|
checkAutoVariables.returnPointerToLocalArray();
|
||||||
|
checkAutoVariables.returnReference();
|
||||||
}
|
}
|
||||||
|
|
||||||
void run()
|
void run()
|
||||||
|
@ -69,6 +70,9 @@ private:
|
||||||
|
|
||||||
TEST_CASE(returnLocalVariable1);
|
TEST_CASE(returnLocalVariable1);
|
||||||
TEST_CASE(returnLocalVariable2);
|
TEST_CASE(returnLocalVariable2);
|
||||||
|
|
||||||
|
// return reference..
|
||||||
|
TEST_CASE(returnReference);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -95,7 +99,8 @@ private:
|
||||||
check("void func1(int* arr[2])\n"
|
check("void func1(int* arr[2])\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
" int num=2;"
|
" 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());
|
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");
|
"}\n");
|
||||||
ASSERT_EQUALS("", errout.str());
|
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)
|
REGISTER_TEST(TestAutoVariables)
|
||||||
|
|
Loading…
Reference in New Issue