Skip to content

Commit 1b9124e

Browse files
Fix #14533, #14536 FN stlcstrConcat, stlcstrAssignment, stlcstrConstructor (#8261)
Co-authored-by: chrchr-github <noreply@github.com>
1 parent 45e0eb0 commit 1b9124e

3 files changed

Lines changed: 91 additions & 11 deletions

File tree

gui/librarydialog.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ void LibraryDialog::saveCfg()
167167
void LibraryDialog::saveCfgAs()
168168
{
169169
const QString filter(tr("Library files (*.cfg)"));
170-
const QString path = Path::getPathFromFilename(mFileName.toStdString()).c_str();
170+
const QString path = QString::fromStdString(Path::getPathFromFilename(mFileName.toStdString()));
171171
QString selectedFile = QFileDialog::getSaveFileName(this,
172172
tr("Save the library as"),
173173
path,

lib/checkstl.cpp

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1959,6 +1959,53 @@ static bool isLocal(const Token *tok)
19591959
return var && !var->isStatic() && var->isLocal();
19601960
}
19611961

1962+
static bool isc_strCall(const Token* tok, const Library::Container* container)
1963+
{
1964+
if (!Token::simpleMatch(tok, "("))
1965+
return false;
1966+
const Token* dot = tok->astOperand1();
1967+
if (!Token::simpleMatch(dot, "."))
1968+
return false;
1969+
const Token* obj = dot->astOperand1();
1970+
if (!obj || !obj->valueType())
1971+
return false;
1972+
const Library::Container* objContainer = obj->valueType()->container;
1973+
if (!objContainer || !container || !objContainer->stdStringLike || (objContainer != container && !container->view))
1974+
return false;
1975+
return Token::Match(dot->astOperand2(), "c_str|data ( )");
1976+
}
1977+
1978+
static bool isc_strConcat(const Token* tok)
1979+
{
1980+
if (!tok->isBinaryOp() || !Token::simpleMatch(tok, "+"))
1981+
return false;
1982+
for (const Token* op : { tok->astOperand1(), tok->astOperand2() }) { // NOLINT(readability-use-anyofallof)
1983+
const Token* sibling = op->astSibling();
1984+
if (!sibling->valueType())
1985+
continue;
1986+
if (isc_strCall(op, sibling->valueType()->container))
1987+
return true;
1988+
}
1989+
return false;
1990+
}
1991+
1992+
static bool isc_strAssignment(const Token* tok)
1993+
{
1994+
if (!Token::simpleMatch(tok, "="))
1995+
return false;
1996+
const Token* strTok = tok->astOperand1();
1997+
if (!strTok || !strTok->valueType())
1998+
return false;
1999+
return isc_strCall(tok->astOperand2(), strTok->valueType()->container);
2000+
}
2001+
2002+
static bool isc_strConstructor(const Token* tok)
2003+
{
2004+
if (!tok->valueType() || !Token::Match(tok, "%var% (|{"))
2005+
return false;
2006+
return isc_strCall(tok->tokAt(1)->astOperand2(), tok->valueType()->container);
2007+
}
2008+
19622009
namespace {
19632010
const std::set<std::string> stl_string_stream = {
19642011
"istringstream", "ostringstream", "stringstream", "wstringstream"
@@ -2027,16 +2074,14 @@ void CheckStl::string_c_str()
20272074
const Variable* var2 = tok->tokAt(2)->variable();
20282075
if (var->isPointer() && var2 && var2->isStlType(stl_string_stream))
20292076
string_c_strError(tok);
2077+
} else if (printPerformance && isc_strAssignment(tok->tokAt(1))) {
2078+
string_c_strAssignment(tok, tok->variable()->getTypeName());
20302079
} else if (Token::Match(tok->tokAt(2), "%name% (") &&
20312080
Token::Match(tok->linkAt(3), ") . c_str|data ( ) ;") &&
20322081
tok->tokAt(2)->function() && Token::Match(tok->tokAt(2)->function()->retDef, "std :: string|wstring %name%")) {
20332082
const Variable* var = tok->variable();
20342083
if (var->isPointer())
20352084
string_c_strError(tok);
2036-
} else if (printPerformance && tok->tokAt(1)->astOperand2() && Token::Match(tok->tokAt(1)->astOperand2()->tokAt(-3), "%var% . c_str|data ( ) ;")) {
2037-
const Token* vartok = tok->tokAt(1)->astOperand2()->tokAt(-3);
2038-
if ((tok->variable()->isStlStringType() || tok->variable()->isStlStringViewType()) && vartok->variable() && vartok->variable()->isStlStringType())
2039-
string_c_strAssignment(tok, tok->variable()->getTypeName());
20402085
}
20412086
} else if (printPerformance && tok->function() && Token::Match(tok, "%name% ( !!)") && tok->str() != scope.className) {
20422087
const auto range = c_strFuncParam.equal_range(tok->function());
@@ -2068,13 +2113,9 @@ void CheckStl::string_c_str()
20682113
}
20692114
}
20702115
}
2071-
} else if (printPerformance && Token::Match(tok, "%var% (|{ %var% . c_str|data ( ) !!,") &&
2072-
tok->variable() && (tok->variable()->isStlStringType() || tok->variable()->isStlStringViewType()) &&
2073-
tok->tokAt(2)->variable() && tok->tokAt(2)->variable()->isStlStringType()) {
2116+
} else if (printPerformance && isc_strConstructor(tok)) {
20742117
string_c_strConstructor(tok, tok->variable()->getTypeName());
2075-
} else if (printPerformance && tok->next() && tok->next()->variable() && tok->next()->variable()->isStlStringType() && tok->valueType() && tok->valueType()->type == ValueType::CONTAINER &&
2076-
((Token::Match(tok->previous(), "%var% + %var% . c_str|data ( )") && tok->previous()->variable() && tok->previous()->variable()->isStlStringType()) ||
2077-
(Token::Match(tok->tokAt(-5), "%var% . c_str|data ( ) + %var%") && tok->tokAt(-5)->variable() && tok->tokAt(-5)->variable()->isStlStringType()))) {
2118+
} else if (printPerformance && isc_strConcat(tok)) {
20782119
string_c_strConcat(tok);
20792120
} else if (printPerformance && Token::simpleMatch(tok, "<<") && tok->astOperand2() && Token::Match(tok->astOperand2()->astOperand1(), ". c_str|data ( )")) {
20802121
const Token* str = tok->astOperand2()->astOperand1()->astOperand1();

test/teststl.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4680,6 +4680,45 @@ class TestStl : public TestFixture {
46804680
" return s->x.c_str();\n"
46814681
"}\n");
46824682
ASSERT_EQUALS("", errout_str());
4683+
4684+
check("std::string f(const std::string& s) {\n" // #14533
4685+
" auto x = std::string(\"abc\") + s.c_str();\n"
4686+
" auto y = s.c_str() + std::string(\"def\");\n"
4687+
" return x + y;\n"
4688+
"}\n");
4689+
ASSERT_EQUALS("[test.cpp:2:33]: (performance) Concatenating the result of c_str() and a std::string is slow and redundant. [stlcstrConcat]\n"
4690+
"[test.cpp:3:24]: (performance) Concatenating the result of c_str() and a std::string is slow and redundant. [stlcstrConcat]\n",
4691+
errout_str());
4692+
4693+
check("std::string get();\n"
4694+
"std::string f(const std::string& s) {\n"
4695+
" return get() + s.c_str();\n"
4696+
"}\n");
4697+
ASSERT_EQUALS("[test.cpp:3:18]: (performance) Concatenating the result of c_str() and a std::string is slow and redundant. [stlcstrConcat]\n",
4698+
errout_str());
4699+
4700+
check("std::string get();\n" // #14536
4701+
" std::string f(const std::string& s) {\n"
4702+
" return s + get().c_str();\n"
4703+
"}\n");
4704+
ASSERT_EQUALS("[test.cpp:3:14]: (performance) Concatenating the result of c_str() and a std::string is slow and redundant. [stlcstrConcat]\n",
4705+
errout_str());
4706+
4707+
check("std::string get();\n"
4708+
"std::string f(std::string & s) {\n"
4709+
" s = get().c_str();\n"
4710+
" std::string s2{ get().c_str() };\n"
4711+
" return s2;\n"
4712+
"}\n");
4713+
ASSERT_EQUALS("[test.cpp:3:5]: (performance) Assigning the result of c_str() to a std::string is slow and redundant. [stlcstrAssignment]\n"
4714+
"[test.cpp:4:17]: (performance) Constructing a std::string from the result of c_str() is slow and redundant. [stlcstrConstructor]\n",
4715+
errout_str());
4716+
4717+
check("void f() {\n"
4718+
" std::string s;\n"
4719+
" auto a = + s.c_str();\n"
4720+
"}\n");
4721+
ASSERT_EQUALS("", errout_str());
46834722
}
46844723

46854724
void uselessCalls() {

0 commit comments

Comments
 (0)