From 5f7d92bd45f6b1c91b51156069c2e77f0e687c21 Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Wed, 25 Feb 2026 12:44:11 +0100 Subject: [PATCH 1/9] Update checkstl.cpp --- lib/checkstl.cpp | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/lib/checkstl.cpp b/lib/checkstl.cpp index c4827157988..a461ddf3deb 100644 --- a/lib/checkstl.cpp +++ b/lib/checkstl.cpp @@ -1959,6 +1959,34 @@ static bool isLocal(const Token *tok) return var && !var->isStatic() && var->isLocal(); } +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 (!Token::simpleMatch(op, "(")) + continue; + const Token* dot = op->astOperand1(); + if (!Token::simpleMatch(dot, ".")) + continue; + if (!Token::Match(dot->astOperand1(), "%var%")) // TODO: function returning string + continue; + const Variable* var = dot->astOperand1()->variable(); + if (!var || !var->isStlStringType()) + continue; + if (!Token::Match(dot->astOperand2(), "c_str|data ( )")) + continue; + cstr = op; + } + if (!cstr) + return false; + const Token* strTok = (cstr == tok->astOperand1()) ? tok->astOperand2() : tok->astOperand1(); + if (strTok->variable() && strTok->variable()->isStlStringType()) + return true; + return strTok->valueType() && strTok->valueType()->container && strTok->valueType()->container->stdStringLike; +} + namespace { const std::set stl_string_stream = { "istringstream", "ostringstream", "stringstream", "wstringstream" @@ -2072,9 +2100,7 @@ void CheckStl::string_c_str() tok->variable() && (tok->variable()->isStlStringType() || tok->variable()->isStlStringViewType()) && tok->tokAt(2)->variable() && tok->tokAt(2)->variable()->isStlStringType()) { 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(); From 0ec922396728b55ec4b182ab296e6f81c28a0666 Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Wed, 25 Feb 2026 12:45:25 +0100 Subject: [PATCH 2/9] Update teststl.cpp --- test/teststl.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/teststl.cpp b/test/teststl.cpp index cc4856628df..5f43442cf86 100644 --- a/test/teststl.cpp +++ b/test/teststl.cpp @@ -4680,6 +4680,22 @@ 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()); } void uselessCalls() { From d4269dd2fdcbc205c6922d39b62bdc46262ee119 Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Wed, 25 Feb 2026 12:49:35 +0100 Subject: [PATCH 3/9] Update checkstl.cpp --- lib/checkstl.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/checkstl.cpp b/lib/checkstl.cpp index a461ddf3deb..1594ce1783e 100644 --- a/lib/checkstl.cpp +++ b/lib/checkstl.cpp @@ -1978,6 +1978,7 @@ static bool isc_strConcat(const Token* tok) if (!Token::Match(dot->astOperand2(), "c_str|data ( )")) continue; cstr = op; + break; } if (!cstr) return false; @@ -2100,7 +2101,7 @@ void CheckStl::string_c_str() tok->variable() && (tok->variable()->isStlStringType() || tok->variable()->isStlStringViewType()) && tok->tokAt(2)->variable() && tok->tokAt(2)->variable()->isStlStringType()) { string_c_strConstructor(tok, tok->variable()->getTypeName()); - else if (printPerformance && isc_strConcat(tok)) { + } 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(); From 4614417c03efcd2c3c4306e3569092e8322369e4 Mon Sep 17 00:00:00 2001 From: chrchr-github Date: Wed, 25 Feb 2026 20:04:54 +0100 Subject: [PATCH 4/9] Fix #14536 FN stlcstrConcat with function returning string --- lib/checkstl.cpp | 12 ++++-------- test/teststl.cpp | 7 +++++++ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/lib/checkstl.cpp b/lib/checkstl.cpp index 1594ce1783e..6b2e3e2b9b5 100644 --- a/lib/checkstl.cpp +++ b/lib/checkstl.cpp @@ -1970,10 +1970,8 @@ static bool isc_strConcat(const Token* tok) const Token* dot = op->astOperand1(); if (!Token::simpleMatch(dot, ".")) continue; - if (!Token::Match(dot->astOperand1(), "%var%")) // TODO: function returning string - continue; - const Variable* var = dot->astOperand1()->variable(); - if (!var || !var->isStlStringType()) + const ValueType* vtObject = dot->astOperand1()->valueType(); + if (!vtObject || !vtObject->container || !vtObject->container->stdStringLike) continue; if (!Token::Match(dot->astOperand2(), "c_str|data ( )")) continue; @@ -1982,10 +1980,8 @@ static bool isc_strConcat(const Token* tok) } if (!cstr) return false; - const Token* strTok = (cstr == tok->astOperand1()) ? tok->astOperand2() : tok->astOperand1(); - if (strTok->variable() && strTok->variable()->isStlStringType()) - return true; - return strTok->valueType() && strTok->valueType()->container && strTok->valueType()->container->stdStringLike; + const ValueType* vtStr = (cstr == tok->astOperand1() ? tok->astOperand2() : tok->astOperand1())->valueType(); + return vtStr && vtStr->container && vtStr->container->stdStringLike; } namespace { diff --git a/test/teststl.cpp b/test/teststl.cpp index 5f43442cf86..b875769ea32 100644 --- a/test/teststl.cpp +++ b/test/teststl.cpp @@ -4696,6 +4696,13 @@ class TestStl : public TestFixture { "}\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()); } void uselessCalls() { From a8bdf5f1154c9ee1876c9c44142a2370d46da985 Mon Sep 17 00:00:00 2001 From: chrchr-github Date: Wed, 25 Feb 2026 22:48:33 +0100 Subject: [PATCH 5/9] Check operand --- lib/checkstl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/checkstl.cpp b/lib/checkstl.cpp index 6b2e3e2b9b5..dd1228fe303 100644 --- a/lib/checkstl.cpp +++ b/lib/checkstl.cpp @@ -1968,7 +1968,7 @@ static bool isc_strConcat(const Token* tok) if (!Token::simpleMatch(op, "(")) continue; const Token* dot = op->astOperand1(); - if (!Token::simpleMatch(dot, ".")) + if (!Token::simpleMatch(dot, ".") || !dot->astOperand1()) continue; const ValueType* vtObject = dot->astOperand1()->valueType(); if (!vtObject || !vtObject->container || !vtObject->container->stdStringLike) From 655952798df617d36911c90b2f2f4987a638be5b Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Thu, 26 Feb 2026 10:18:15 +0100 Subject: [PATCH 6/9] Update checkstl.cpp --- lib/checkstl.cpp | 62 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/lib/checkstl.cpp b/lib/checkstl.cpp index dd1228fe303..19c27531976 100644 --- a/lib/checkstl.cpp +++ b/lib/checkstl.cpp @@ -1959,29 +1959,53 @@ 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 (!Token::simpleMatch(op, "(")) - continue; - const Token* dot = op->astOperand1(); - if (!Token::simpleMatch(dot, ".") || !dot->astOperand1()) - continue; - const ValueType* vtObject = dot->astOperand1()->valueType(); - if (!vtObject || !vtObject->container || !vtObject->container->stdStringLike) - continue; - if (!Token::Match(dot->astOperand2(), "c_str|data ( )")) - continue; - cstr = op; - break; + if (isc_strCall(op)) { + cstr = op; + break; + } } if (!cstr) return false; - const ValueType* vtStr = (cstr == tok->astOperand1() ? tok->astOperand2() : tok->astOperand1())->valueType(); - return vtStr && vtStr->container && vtStr->container->stdStringLike; + 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 { @@ -2052,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()); @@ -2093,9 +2115,7 @@ 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 && isc_strConcat(tok)) { string_c_strConcat(tok); From 1eafc89d7f76fdceb62ee80fd9f0db7fbb3cf167 Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Thu, 26 Feb 2026 10:19:11 +0100 Subject: [PATCH 7/9] Update teststl.cpp --- test/teststl.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/teststl.cpp b/test/teststl.cpp index b875769ea32..fb0689e67c2 100644 --- a/test/teststl.cpp +++ b/test/teststl.cpp @@ -4703,6 +4703,16 @@ class TestStl : public TestFixture { "}\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() { From 82b2a0f89042134ece453c43166a14e61ecd1a9f Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Thu, 26 Feb 2026 11:38:37 +0100 Subject: [PATCH 8/9] Update teststl.cpp --- test/teststl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/teststl.cpp b/test/teststl.cpp index fb0689e67c2..aafdf353a60 100644 --- a/test/teststl.cpp +++ b/test/teststl.cpp @@ -4703,7 +4703,7 @@ class TestStl : public TestFixture { "}\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" From f6b00d69b886868b12b0063c23fa64d0005fb2b5 Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Thu, 26 Feb 2026 11:39:58 +0100 Subject: [PATCH 9/9] Update librarydialog.cpp --- gui/librarydialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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,