diff --git a/gui/librarydialog.cpp b/gui/librarydialog.cpp index e48e68e1c88..085339bfc75 100644 --- a/gui/librarydialog.cpp +++ b/gui/librarydialog.cpp @@ -167,7 +167,7 @@ void LibraryDialog::saveCfg() void LibraryDialog::saveCfgAs() { const QString filter(tr("Library files (*.cfg)")); - const QString path = Path::getPathFromFilename(mFileName.toStdString()).c_str(); + const QString path = QString::fromStdString(Path::getPathFromFilename(mFileName.toStdString())); QString selectedFile = QFileDialog::getSaveFileName(this, tr("Save the library as"), path, diff --git a/lib/checkstl.cpp b/lib/checkstl.cpp index c4827157988..19c27531976 100644 --- a/lib/checkstl.cpp +++ b/lib/checkstl.cpp @@ -1959,6 +1959,55 @@ static bool isLocal(const Token *tok) return var && !var->isStatic() && var->isLocal(); } +static bool isc_strCall(const Token* tok) +{ + if (!Token::simpleMatch(tok, "(")) + return false; + const Token* dot = tok->astOperand1(); + if (!Token::simpleMatch(dot, ".")) + return false; + const Token* obj = dot->astOperand1(); + if (!obj || !obj->valueType() || !obj->valueType()->container || !obj->valueType()->container->stdStringLike) + return false; + return Token::Match(dot->astOperand2(), "c_str|data ( )"); +} + +static bool isc_strConcat(const Token* tok) +{ + if (!Token::simpleMatch(tok, "+")) + return false; + const Token* cstr = nullptr; + for (const Token* op : { tok->astOperand1(), tok->astOperand2() }) { + if (isc_strCall(op)) { + cstr = op; + break; + } + } + if (!cstr) + return false; + const Token* strTok = (cstr == tok->astOperand1()) ? tok->astOperand2() : tok->astOperand1(); + return strTok->valueType() && strTok->valueType()->container && strTok->valueType()->container->stdStringLike; +} + +static bool isc_strAssignment(const Token* tok) +{ + if (!Token::simpleMatch(tok, "=")) + return false; + if (!isc_strCall(tok->astOperand2())) + return false; + const Token* strTok = tok->astOperand1(); + return strTok && strTok->valueType() && strTok->valueType()->container && strTok->valueType()->container->stdStringLike; +} + +static bool isc_strConstructor(const Token* tok) +{ + if (!Token::Match(tok, "%var% (|{")) + return false; + if (!isc_strCall(tok->tokAt(1)->astOperand2())) + return false; + return tok->valueType() && tok->valueType()->container && tok->valueType()->container->stdStringLike; +} + namespace { const std::set stl_string_stream = { "istringstream", "ostringstream", "stringstream", "wstringstream" @@ -2027,16 +2076,14 @@ void CheckStl::string_c_str() const Variable* var2 = tok->tokAt(2)->variable(); if (var->isPointer() && var2 && var2->isStlType(stl_string_stream)) string_c_strError(tok); + } else if (printPerformance && isc_strAssignment(tok->tokAt(1))) { + string_c_strAssignment(tok, tok->variable()->getTypeName()); } else if (Token::Match(tok->tokAt(2), "%name% (") && Token::Match(tok->linkAt(3), ") . c_str|data ( ) ;") && tok->tokAt(2)->function() && Token::Match(tok->tokAt(2)->function()->retDef, "std :: string|wstring %name%")) { const Variable* var = tok->variable(); if (var->isPointer()) string_c_strError(tok); - } else if (printPerformance && tok->tokAt(1)->astOperand2() && Token::Match(tok->tokAt(1)->astOperand2()->tokAt(-3), "%var% . c_str|data ( ) ;")) { - const Token* vartok = tok->tokAt(1)->astOperand2()->tokAt(-3); - if ((tok->variable()->isStlStringType() || tok->variable()->isStlStringViewType()) && vartok->variable() && vartok->variable()->isStlStringType()) - string_c_strAssignment(tok, tok->variable()->getTypeName()); } } else if (printPerformance && tok->function() && Token::Match(tok, "%name% ( !!)") && tok->str() != scope.className) { const auto range = c_strFuncParam.equal_range(tok->function()); @@ -2068,13 +2115,9 @@ void CheckStl::string_c_str() } } } - } else if (printPerformance && Token::Match(tok, "%var% (|{ %var% . c_str|data ( ) !!,") && - tok->variable() && (tok->variable()->isStlStringType() || tok->variable()->isStlStringViewType()) && - tok->tokAt(2)->variable() && tok->tokAt(2)->variable()->isStlStringType()) { + } else if (printPerformance && isc_strConstructor(tok)) { string_c_strConstructor(tok, tok->variable()->getTypeName()); - } else if (printPerformance && tok->next() && tok->next()->variable() && tok->next()->variable()->isStlStringType() && tok->valueType() && tok->valueType()->type == ValueType::CONTAINER && - ((Token::Match(tok->previous(), "%var% + %var% . c_str|data ( )") && tok->previous()->variable() && tok->previous()->variable()->isStlStringType()) || - (Token::Match(tok->tokAt(-5), "%var% . c_str|data ( ) + %var%") && tok->tokAt(-5)->variable() && tok->tokAt(-5)->variable()->isStlStringType()))) { + } else if (printPerformance && isc_strConcat(tok)) { string_c_strConcat(tok); } else if (printPerformance && Token::simpleMatch(tok, "<<") && tok->astOperand2() && Token::Match(tok->astOperand2()->astOperand1(), ". c_str|data ( )")) { const Token* str = tok->astOperand2()->astOperand1()->astOperand1(); diff --git a/test/teststl.cpp b/test/teststl.cpp index cc4856628df..aafdf353a60 100644 --- a/test/teststl.cpp +++ b/test/teststl.cpp @@ -4680,6 +4680,39 @@ class TestStl : public TestFixture { " return s->x.c_str();\n" "}\n"); ASSERT_EQUALS("", errout_str()); + + check("std::string f(const std::string& s) {\n" // #14533 + " auto x = std::string(\"abc\") + s.c_str();\n" + " auto y = s.c_str() + std::string(\"def\");\n" + " return x + y;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2:33]: (performance) Concatenating the result of c_str() and a std::string is slow and redundant. [stlcstrConcat]\n" + "[test.cpp:3:24]: (performance) Concatenating the result of c_str() and a std::string is slow and redundant. [stlcstrConcat]\n", + errout_str()); + + check("std::string get();\n" + "std::string f(const std::string& s) {\n" + " return get() + s.c_str();\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3:18]: (performance) Concatenating the result of c_str() and a std::string is slow and redundant. [stlcstrConcat]\n", + errout_str()); + + check("std::string get();\n" // #14536 + " std::string f(const std::string& s) {\n" + " return s + get().c_str();\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3:14]: (performance) Concatenating the result of c_str() and a std::string is slow and redundant. [stlcstrConcat]\n", + errout_str()); + + check("std::string get();\n" + "std::string f(std::string & s) {\n" + " s = get().c_str();\n" + " std::string s2{ get().c_str() };\n" + " return s2;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3:5]: (performance) Assigning the result of c_str() to a std::string is slow and redundant. [stlcstrAssignment]\n" + "[test.cpp:4:17]: (performance) Constructing a std::string from the result of c_str() is slow and redundant. [stlcstrConstructor]\n", + errout_str()); } void uselessCalls() {