Auto variables: returning pointer to temporary .c_str() data

This commit is contained in:
Daniel Marjamäki 2010-01-26 22:11:34 +01:00
parent fa305d70bc
commit 32fcb8fe18
3 changed files with 133 additions and 1 deletions

View File

@ -355,6 +355,10 @@ void CheckAutoVariables::returnReference()
// goto next token.. // goto next token..
tok2 = tok2->next(); tok2 = tok2->next();
// skip "const"
if (Token::Match(tok2, "const %type%"))
tok2 = tok2->next();
// skip "std::" if it is seen // skip "std::" if it is seen
if (Token::simpleMatch(tok2, "std ::")) if (Token::simpleMatch(tok2, "std ::"))
tok2 = tok2->tokAt(2); 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");
}

View File

@ -59,6 +59,9 @@ public:
/** Returning reference to local/temporary variable */ /** Returning reference to local/temporary variable */
void returnReference(); void returnReference();
/** Returning c_str to local variable */
void returncstr();
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;
@ -74,12 +77,14 @@ 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 errorReturnReference(const Token *tok);
void errorReturnAutocstr(const Token *tok);
void getErrorMessages() void getErrorMessages()
{ {
errorAutoVariableAssignment(0); errorAutoVariableAssignment(0);
errorReturnPointerToLocalArray(0); errorReturnPointerToLocalArray(0);
errorReturnReference(0); errorReturnReference(0);
errorReturnAutocstr(0);
} }
std::string name() const 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" 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 auto 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"; "* returning reference to local/temporary variable\n";
} }

View File

@ -58,6 +58,7 @@ private:
checkAutoVariables.autoVariables(); checkAutoVariables.autoVariables();
checkAutoVariables.returnPointerToLocalArray(); checkAutoVariables.returnPointerToLocalArray();
checkAutoVariables.returnReference(); checkAutoVariables.returnReference();
checkAutoVariables.returncstr();
} }
void run() void run()
@ -73,6 +74,9 @@ private:
// return reference.. // return reference..
TEST_CASE(returnReference); TEST_CASE(returnReference);
// return c_str()..
TEST_CASE(returncstr);
} }
@ -188,7 +192,41 @@ private:
"}\n"); "}\n");
ASSERT_EQUALS("", errout.str()); 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) REGISTER_TEST(TestAutoVariables)