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..
|
// 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");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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";
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue