New Check: Detect return of invalid reference

This commit is contained in:
Daniel Marjamäki 2010-01-23 20:39:12 +01:00
parent eda60f6483
commit f62466493b
3 changed files with 122 additions and 4 deletions

View File

@ -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");
}

View File

@ -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";
} }
}; };
/// @} /// @}

View File

@ -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]=&num;}"); " arr[0]=&num;\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)