Skip to content

Commit b04bf73

Browse files
Fix #7515 New check: Not needed c_str() operation (#4371)
1 parent d73a33d commit b04bf73

6 files changed

Lines changed: 49 additions & 4 deletions

File tree

lib/astutils.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,12 @@ bool isVariableDecl(const Token* tok)
380380
return false;
381381
}
382382

383+
bool isStlStringType(const Token* tok)
384+
{
385+
return Token::Match(tok, "std :: string|wstring|u16string|u32string !!::") ||
386+
(Token::simpleMatch(tok, "std :: basic_string <") && !Token::simpleMatch(tok->linkAt(3), "> ::"));
387+
}
388+
383389
bool isTemporary(bool cpp, const Token* tok, const Library* library, bool unknown)
384390
{
385391
if (!tok)

lib/astutils.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ std::string astCanonicalType(const Token *expr);
162162
const Token * astIsVariableComparison(const Token *tok, const std::string &comp, const std::string &rhs, const Token **vartok=nullptr);
163163

164164
bool isVariableDecl(const Token* tok);
165+
bool isStlStringType(const Token* tok);
165166

166167
bool isTemporary(bool cpp, const Token* tok, const Library* library, bool unknown = false);
167168

lib/checkstl.cpp

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2009,6 +2009,18 @@ void CheckStl::string_c_str()
20092009
((Token::Match(tok->previous(), "%var% + %var% . c_str|data ( )") && tok->previous()->variable() && tok->previous()->variable()->isStlStringType()) ||
20102010
(Token::Match(tok->tokAt(-5), "%var% . c_str|data ( ) + %var%") && tok->tokAt(-5)->variable() && tok->tokAt(-5)->variable()->isStlStringType()))) {
20112011
string_c_strConcat(tok);
2012+
} else if (printPerformance && Token::simpleMatch(tok, "<<") && tok->astOperand2() && Token::simpleMatch(tok->astOperand2()->astOperand1(), ". c_str ( )")) {
2013+
const Token* str = tok->astOperand2()->astOperand1()->astOperand1();
2014+
if (Token::Match(str, "(|["))
2015+
str = str->previous();
2016+
if (str && ((str->variable() && str->variable()->isStlStringType()) ||
2017+
(str->function() && isStlStringType(str->function()->retDef)))) {
2018+
const Token* strm = tok;
2019+
while (Token::simpleMatch(strm, "<<"))
2020+
strm = strm->astOperand1();
2021+
if (strm && strm->variable() && strm->variable()->isStlType())
2022+
string_c_strStream(tok);
2023+
}
20122024
}
20132025

20142026
// Using c_str() to get the return value is only dangerous if the function returns a char*
@@ -2118,22 +2130,32 @@ void CheckStl::string_c_strParam(const Token* tok, nonneg int number)
21182130

21192131
void CheckStl::string_c_strConstructor(const Token* tok)
21202132
{
2121-
std::string msg = "Constructing a std::string from the result of c_str() is slow and redundant.\nSolve that by directly passing the string.";
2133+
std::string msg = "Constructing a std::string from the result of c_str() is slow and redundant.\n"
2134+
"Constructing a std::string from const char* requires a call to strlen(). Solve that by directly passing the string.";
21222135
reportError(tok, Severity::performance, "stlcstrConstructor", msg, CWE704, Certainty::normal);
21232136
}
21242137

21252138
void CheckStl::string_c_strAssignment(const Token* tok)
21262139
{
2127-
std::string msg = "Assigning the result of c_str() to a std::string is slow and redundant.\nSolve that by directly assigning the string.";
2140+
std::string msg = "Assigning the result of c_str() to a std::string is slow and redundant.\n"
2141+
"Assigning a const char* to a std::string requires a call to strlen(). Solve that by directly assigning the string.";
21282142
reportError(tok, Severity::performance, "stlcstrAssignment", msg, CWE704, Certainty::normal);
21292143
}
21302144

21312145
void CheckStl::string_c_strConcat(const Token* tok)
21322146
{
2133-
std::string msg = "Concatenating the result of c_str() and a std::string is slow and redundant.\nSolve that by directly concatenating the strings.";
2147+
std::string msg = "Concatenating the result of c_str() and a std::string is slow and redundant.\n"
2148+
"Concatenating a const char* with a std::string requires a call to strlen(). Solve that by directly concatenating the strings.";
21342149
reportError(tok, Severity::performance, "stlcstrConcat", msg, CWE704, Certainty::normal);
21352150
}
21362151

2152+
void CheckStl::string_c_strStream(const Token* tok)
2153+
{
2154+
std::string msg = "Passing the result of c_str() to a stream is slow and redundant.\n"
2155+
"Passing a const char* to a stream requires a call to strlen(). Solve that by directly passing the string.";
2156+
reportError(tok, Severity::performance, "stlcstrStream", msg, CWE704, Certainty::normal);
2157+
}
2158+
21372159
//---------------------------------------------------------------------------
21382160
//
21392161
//---------------------------------------------------------------------------

lib/checkstl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ class CPPCHECKLIB CheckStl : public Check {
196196
void string_c_strConstructor(const Token* tok);
197197
void string_c_strAssignment(const Token* tok);
198198
void string_c_strConcat(const Token* tok);
199+
void string_c_strStream(const Token* tok);
199200

200201
void outOfBoundsError(const Token *tok, const std::string &containerName, const ValueFlow::Value *containerSize, const std::string &index, const ValueFlow::Value *indexValue);
201202
void outOfBoundsIndexExpressionError(const Token *tok, const Token *index);

lib/symboldatabase.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2187,7 +2187,7 @@ void Variable::evaluate(const Settings* settings)
21872187
strtype += "::" + typeToken->strAt(2);
21882188
setFlag(fIsClass, !lib->podtype(strtype) && !mTypeStartToken->isStandardType() && !isEnumType() && !isPointer() && !isReference() && strtype != "...");
21892189
setFlag(fIsStlType, Token::simpleMatch(mTypeStartToken, "std ::"));
2190-
setFlag(fIsStlString, isStlType() && (Token::Match(mTypeStartToken->tokAt(2), "string|wstring|u16string|u32string !!::") || (Token::simpleMatch(mTypeStartToken->tokAt(2), "basic_string <") && !Token::simpleMatch(mTypeStartToken->linkAt(3), "> ::"))));
2190+
setFlag(fIsStlString, ::isStlStringType(mTypeStartToken));
21912191
setFlag(fIsSmartPointer, lib->isSmartPointer(mTypeStartToken));
21922192
}
21932193
if (mAccess == AccessControl::Argument) {

test/teststl.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4067,6 +4067,21 @@ class TestStl : public TestFixture {
40674067
" const double* const QM_R__ buf(v.data() + i);\n"
40684068
"}\n");
40694069
ASSERT_EQUALS("", errout.str());
4070+
4071+
check("struct T { std::string g(); std::string a[1]; }\n" // #7515
4072+
"void f(std::stringstream& strm, const std::string& s, T& t) {\n"
4073+
" strm << s.c_str();\n"
4074+
" strm << \"abc\" << s.c_str();\n"
4075+
" strm << \"abc\" << s.c_str() << \"def\";\n"
4076+
" strm << \"abc\" << t.g().c_str() << \"def\";\n"
4077+
" strm << t.a[0].c_str();\n"
4078+
"}\n");
4079+
ASSERT_EQUALS("[test.cpp:3]: (performance) Passing the result of c_str() to a stream is slow and redundant.\n"
4080+
"[test.cpp:4]: (performance) Passing the result of c_str() to a stream is slow and redundant.\n"
4081+
"[test.cpp:5]: (performance) Passing the result of c_str() to a stream is slow and redundant.\n"
4082+
"[test.cpp:6]: (performance) Passing the result of c_str() to a stream is slow and redundant.\n"
4083+
"[test.cpp:7]: (performance) Passing the result of c_str() to a stream is slow and redundant.\n",
4084+
errout.str());
40704085
}
40714086

40724087
void uselessCalls() {

0 commit comments

Comments
 (0)