Auto variables: returning pointer to temporary .c_str() data
This commit is contained in:
parent
fa305d70bc
commit
32fcb8fe18
|
@ -355,6 +355,10 @@ void CheckAutoVariables::returnReference()
|
|||
// goto next token..
|
||||
tok2 = tok2->next();
|
||||
|
||||
// skip "const"
|
||||
if (Token::Match(tok2, "const %type%"))
|
||||
tok2 = tok2->next();
|
||||
|
||||
// skip "std::" if it is seen
|
||||
if (Token::simpleMatch(tok2, "std ::"))
|
||||
tok2 = tok2->tokAt(2);
|
||||
|
@ -389,3 +393,88 @@ void CheckAutoVariables::errorReturnReference(const Token *tok)
|
|||
}
|
||||
|
||||
|
||||
// Return c_str
|
||||
void CheckAutoVariables::returncstr()
|
||||
{
|
||||
// locate function that returns a const char *..
|
||||
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, "const char * %var% ("))
|
||||
{
|
||||
// go to the '('
|
||||
const Token *tok2 = tok->tokAt(4);
|
||||
|
||||
// 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 "const"
|
||||
if (Token::Match(tok2, "const %type%"))
|
||||
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());
|
||||
}
|
||||
|
||||
// return..
|
||||
else if (Token::Match(tok2, "return %var% . c_str ( ) ;"))
|
||||
{
|
||||
// is the returned variable a local variable?
|
||||
if ((tok2->next()->varId() > 0) &&
|
||||
(localvar.find(tok2->next()->varId()) != localvar.end()))
|
||||
{
|
||||
// report error..
|
||||
errorReturnAutocstr(tok2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CheckAutoVariables::errorReturnAutocstr(const Token *tok)
|
||||
{
|
||||
reportError(tok, Severity::error, "returnAutocstr", "Returning pointer to auto variable");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -59,6 +59,9 @@ public:
|
|||
/** Returning reference to local/temporary variable */
|
||||
void returnReference();
|
||||
|
||||
/** Returning c_str to local variable */
|
||||
void returncstr();
|
||||
|
||||
private:
|
||||
std::set<std::string> fp_list;
|
||||
std::set<unsigned int> vd_list;
|
||||
|
@ -74,12 +77,14 @@ private:
|
|||
void errorReturnPointerToLocalArray(const Token *tok);
|
||||
void errorAutoVariableAssignment(const Token *tok);
|
||||
void errorReturnReference(const Token *tok);
|
||||
void errorReturnAutocstr(const Token *tok);
|
||||
|
||||
void getErrorMessages()
|
||||
{
|
||||
errorAutoVariableAssignment(0);
|
||||
errorReturnPointerToLocalArray(0);
|
||||
errorReturnReference(0);
|
||||
errorReturnAutocstr(0);
|
||||
}
|
||||
|
||||
std::string name() const
|
||||
|
@ -91,7 +96,7 @@ 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"
|
||||
"* returning a pointer to auto variable\n"
|
||||
"* assigning address of an variable to an effective parameter of a function\n"
|
||||
"* returning reference to local/temporary variable\n";
|
||||
}
|
||||
|
|
|
@ -58,6 +58,7 @@ private:
|
|||
checkAutoVariables.autoVariables();
|
||||
checkAutoVariables.returnPointerToLocalArray();
|
||||
checkAutoVariables.returnReference();
|
||||
checkAutoVariables.returncstr();
|
||||
}
|
||||
|
||||
void run()
|
||||
|
@ -73,6 +74,9 @@ private:
|
|||
|
||||
// return reference..
|
||||
TEST_CASE(returnReference);
|
||||
|
||||
// return c_str()..
|
||||
TEST_CASE(returncstr);
|
||||
}
|
||||
|
||||
|
||||
|
@ -188,7 +192,41 @@ private:
|
|||
"}\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
check("std::string hello()\n"
|
||||
"{\n"
|
||||
" return \"hello\";\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"std::string &f()\n"
|
||||
"{\n"
|
||||
" return hello();\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
TODO_ASSERT_EQUALS("[test.cpp:5]: (error) Returning reference to temporary\n", errout.str());
|
||||
}
|
||||
|
||||
void returncstr()
|
||||
{
|
||||
check("const char *foo()\n"
|
||||
"{\n"
|
||||
" std::string s;\n"
|
||||
" return s.c_str();\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS("[test.cpp:4]: (error) Returning pointer to auto variable\n", errout.str());
|
||||
|
||||
check("std::string hello()\n"
|
||||
"{\n"
|
||||
" return \"hello\";\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"const char *f()\n"
|
||||
"{\n"
|
||||
" return hello().c_str();\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
TODO_ASSERT_EQUALS("[test.cpp:5]: (error) Returning pointer to temporary\n", errout.str());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
REGISTER_TEST(TestAutoVariables)
|
||||
|
|
Loading…
Reference in New Issue