STL: check for dangerous usage of string::c_str(). Ticket: #1116
This commit is contained in:
parent
3faaa397e9
commit
e54129fa8d
|
@ -918,3 +918,49 @@ void CheckStl::missingComparisonError(const Token *incrementToken1, const Token
|
|||
}
|
||||
|
||||
|
||||
void CheckStl::string_c_str()
|
||||
{
|
||||
// Try to detect common problems when using string::c_str()
|
||||
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next())
|
||||
{
|
||||
// Locate executable scopes:
|
||||
if (Token::Match(tok, ") const| {"))
|
||||
{
|
||||
std::set<unsigned int> localvar;
|
||||
|
||||
// scan through this executable scope:
|
||||
unsigned int indentlevel = 0;
|
||||
while (NULL != (tok = tok->next()))
|
||||
{
|
||||
if (tok->str() == "{")
|
||||
++indentlevel;
|
||||
else if (tok->str() == "}")
|
||||
{
|
||||
if (indentlevel <= 1)
|
||||
break;
|
||||
--indentlevel;
|
||||
}
|
||||
|
||||
// Variable declarations..
|
||||
else if (Token::Match(tok->previous(), "[;{}] std :: %type% %var% ;"))
|
||||
localvar.insert(tok->tokAt(3)->varId());
|
||||
else if (Token::Match(tok->previous(), "[;{}] %type% %var% ;"))
|
||||
localvar.insert(tok->next()->varId());
|
||||
|
||||
// Invalid usage..
|
||||
else if (Token::Match(tok, "throw %var% . c_str ( ) ;") &&
|
||||
tok->next()->varId() > 0 &&
|
||||
localvar.find(tok->next()->varId()) != localvar.end())
|
||||
{
|
||||
string_c_strError(tok);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CheckStl::string_c_strError(const Token *tok)
|
||||
{
|
||||
reportError(tok, Severity::error, "stlcstr", "Dangerous usage of c_str()");
|
||||
}
|
||||
|
||||
|
|
|
@ -55,6 +55,7 @@ public:
|
|||
checkStl.pushback();
|
||||
checkStl.stlBoundries();
|
||||
checkStl.if_find();
|
||||
checkStl.string_c_str();
|
||||
|
||||
// Style check
|
||||
checkStl.size();
|
||||
|
@ -129,6 +130,10 @@ public:
|
|||
void missingComparison();
|
||||
void missingComparisonError(const Token *incrementToken1, const Token *incrementToken2);
|
||||
|
||||
/** Check for common mistakes when using the function string::c_str() */
|
||||
void string_c_str();
|
||||
void string_c_strError(const Token *tok);
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
|
@ -163,6 +168,7 @@ private:
|
|||
stlBoundriesError(0, "container");
|
||||
if_findError(0, false);
|
||||
if_findError(0, true);
|
||||
string_c_strError(0);
|
||||
sizeError(0);
|
||||
eraseByValueError(0, "container", "iterator");
|
||||
redundantIfRemoveError(0);
|
||||
|
@ -183,7 +189,8 @@ private:
|
|||
"* for vectors: using iterator/pointer after push_back has been used\n"
|
||||
"* optimisation: use empty() instead of size() to guarantee fast code\n"
|
||||
"* suspicious condition when using find\n"
|
||||
"* redundant condition\n";
|
||||
"* redundant condition\n"
|
||||
"* common mistakes when using string::c_str()";
|
||||
}
|
||||
|
||||
bool isStlContainer(const Token *tok);
|
||||
|
|
|
@ -98,6 +98,9 @@ private:
|
|||
TEST_CASE(missingInnerComparison1);
|
||||
TEST_CASE(missingInnerComparison2); // no FP when there is comparison
|
||||
TEST_CASE(missingInnerComparison3); // no FP when there is iterator shadowing
|
||||
|
||||
// catch common problems when using the string::c_str() function
|
||||
TEST_CASE(cstr);
|
||||
}
|
||||
|
||||
void check(const std::string &code)
|
||||
|
@ -1090,6 +1093,22 @@ private:
|
|||
"}\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
void cstr()
|
||||
{
|
||||
check("void f() {\n"
|
||||
" std::string errmsg;\n"
|
||||
" throw errmsg.c_str();\n"
|
||||
"}");
|
||||
ASSERT_EQUALS("[test.cpp:3]: (error) Dangerous usage of c_str()\n", errout.str());
|
||||
|
||||
check("std::string f();\n"
|
||||
"\n"
|
||||
"void foo() {\n"
|
||||
" const char *c = f().c_str();\n"
|
||||
"}");
|
||||
TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Dangerous usage of c_str()\n", errout.str());
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_TEST(TestStl)
|
||||
|
|
Loading…
Reference in New Issue