From 6d33b3ed63d85119ade6a651a846259d09703466 Mon Sep 17 00:00:00 2001 From: chrchr-github Date: Sat, 14 Mar 2026 10:19:14 +0100 Subject: [PATCH 1/4] Fix #14600 FN stlcstrConstructor with offset (regression) --- lib/checkstl.cpp | 15 ++++++++++++++- test/teststl.cpp | 7 +++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/lib/checkstl.cpp b/lib/checkstl.cpp index 882f4588eeb..b0bf1983d10 100644 --- a/lib/checkstl.cpp +++ b/lib/checkstl.cpp @@ -2003,7 +2003,20 @@ static bool isc_strConstructor(const Token* tok) { if (!tok->valueType() || !Token::Match(tok, "%var% (|{")) return false; - return isc_strCall(tok->tokAt(1)->astOperand2(), tok->valueType()->container); + + const Token* callTok = tok->tokAt(1)->astOperand2(); + while (Token::simpleMatch(callTok, "+") && callTok->isBinaryOp()) { + if (callTok->astOperand1()->str() == "+") { + callTok = callTok->astOperand1(); + continue; + } + if (callTok->astOperand1()->str() == "(") + callTok = callTok->astOperand1(); + else if (tok->astOperand2()->str() == "(") + callTok = callTok->astOperand2(); + break; + } + return isc_strCall(callTok, tok->valueType()->container); } namespace { diff --git a/test/teststl.cpp b/test/teststl.cpp index 2c88f17dd14..29cd85f5759 100644 --- a/test/teststl.cpp +++ b/test/teststl.cpp @@ -4719,6 +4719,13 @@ class TestStl : public TestFixture { " auto a = + s.c_str();\n" "}\n"); ASSERT_EQUALS("", errout_str()); + + check("std::string f(const std::string& a) {\n" // #14600 + " std::string b(a.c_str() + 1 + 2);\n" + " return b;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2:17]: (performance) Constructing a std::string from the result of c_str() is slow and redundant. [stlcstrConstructor]\n", + errout_str()); } void uselessCalls() { From a4b7dbcac1311012372f525fa10cc6067cf22028 Mon Sep 17 00:00:00 2001 From: chrchr-github Date: Sat, 14 Mar 2026 12:39:50 +0100 Subject: [PATCH 2/4] Fix --- lib/checkstl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/checkstl.cpp b/lib/checkstl.cpp index b0bf1983d10..566f30a202d 100644 --- a/lib/checkstl.cpp +++ b/lib/checkstl.cpp @@ -2012,7 +2012,7 @@ static bool isc_strConstructor(const Token* tok) } if (callTok->astOperand1()->str() == "(") callTok = callTok->astOperand1(); - else if (tok->astOperand2()->str() == "(") + else if (callTok->astOperand2()->str() == "(") callTok = callTok->astOperand2(); break; } From 4d57075f14ffcdcb62b20f31f933e48c68abb105 Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Thu, 26 Mar 2026 17:16:27 +0100 Subject: [PATCH 3/4] Update checkstl.cpp --- lib/checkstl.cpp | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/lib/checkstl.cpp b/lib/checkstl.cpp index 566f30a202d..617f796f2f0 100644 --- a/lib/checkstl.cpp +++ b/lib/checkstl.cpp @@ -2003,20 +2003,17 @@ static bool isc_strConstructor(const Token* tok) { if (!tok->valueType() || !Token::Match(tok, "%var% (|{")) return false; - - const Token* callTok = tok->tokAt(1)->astOperand2(); - while (Token::simpleMatch(callTok, "+") && callTok->isBinaryOp()) { - if (callTok->astOperand1()->str() == "+") { - callTok = callTok->astOperand1(); - continue; + bool result = false; + visitAstNodes(tok->tokAt(1)->astOperand2(), [&](const Token* tok2) { + if (Token::simpleMatch(tok2, "+")) + return ChildrenToVisit::op1_and_op2; + if (isc_strCall(tok2, tok->valueType()->container)) { + result = true; + return ChildrenToVisit::done; } - if (callTok->astOperand1()->str() == "(") - callTok = callTok->astOperand1(); - else if (callTok->astOperand2()->str() == "(") - callTok = callTok->astOperand2(); - break; - } - return isc_strCall(callTok, tok->valueType()->container); + return ChildrenToVisit::none; + }); + return result; } namespace { From 9259f1e3053ccff10d1a558be44aad63fe51a213 Mon Sep 17 00:00:00 2001 From: chrchr-github Date: Fri, 27 Mar 2026 23:08:15 +0100 Subject: [PATCH 4/4] Helper function --- lib/checkstl.cpp | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/lib/checkstl.cpp b/lib/checkstl.cpp index 617f796f2f0..65bf3792615 100644 --- a/lib/checkstl.cpp +++ b/lib/checkstl.cpp @@ -1989,6 +1989,23 @@ static bool isc_strConcat(const Token* tok) return false; } +static bool isc_strInPlusChain(const Token* tok, const Library::Container* container) +{ + if (!tok || !tok->valueType() || !tok->valueType()->pointer) + return false; + bool result = false; + visitAstNodes(tok, [&](const Token* tok2) { + if (Token::simpleMatch(tok2, "+")) + return ChildrenToVisit::op1_and_op2; + if (isc_strCall(tok2, container)) { + result = true; + return ChildrenToVisit::done; + } + return ChildrenToVisit::none; + }); + return result; +} + static bool isc_strAssignment(const Token* tok) { if (!Token::simpleMatch(tok, "=")) @@ -2003,17 +2020,7 @@ static bool isc_strConstructor(const Token* tok) { if (!tok->valueType() || !Token::Match(tok, "%var% (|{")) return false; - bool result = false; - visitAstNodes(tok->tokAt(1)->astOperand2(), [&](const Token* tok2) { - if (Token::simpleMatch(tok2, "+")) - return ChildrenToVisit::op1_and_op2; - if (isc_strCall(tok2, tok->valueType()->container)) { - result = true; - return ChildrenToVisit::done; - } - return ChildrenToVisit::none; - }); - return result; + return isc_strInPlusChain(tok->tokAt(1)->astOperand2(), tok->valueType()->container); } namespace {