Skip to content

Commit 725c431

Browse files
Fix #11881 FP returnStdMoveLocal / Fix FP incorrectStringBooleanError / Support std::string::starts/ends_with() (#5347)
1 parent 03b952d commit 725c431

8 files changed

Lines changed: 44 additions & 27 deletions

File tree

cfg/std.cfg

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6884,6 +6884,18 @@ The obsolete function 'gets' is called. With 'gets' you'll get a buffer overrun
68846884
<not-uninit/>
68856885
</arg>
68866886
</function>
6887+
<!-- constexpr bool std::string::starts_with( std::basic_string_view<CharT,Traits> sv ) const noexcept; // since C++20 -->
6888+
<!-- constexpr bool starts_with( CharT ch ) const noexcept; // since C++20 -->
6889+
<!-- constexpr bool starts_with( const CharT* s ) const; // since C++20 -->
6890+
<function name="std::string::starts_with,std::wstring::starts_with,std::string::ends_with,std::wstring::ends_with">
6891+
<noreturn>false</noreturn>
6892+
<use-retval/>
6893+
<returnValue type="bool"/>
6894+
<const/>
6895+
<arg nr="1" direction="in">
6896+
<not-uninit/>
6897+
</arg>
6898+
</function>
68876899
<!-- size_type std::string::copy( CharT* dest, size_type count, size_type pos = 0) const; -->
68886900
<!-- size_type std::wstring::copy( CharT* dest, size_type count, size_type pos = 0) const; -->
68896901
<function name="std::string::copy,std::wstring::copy">

lib/astutils.cpp

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1477,19 +1477,6 @@ static bool astIsBoolLike(const Token* tok)
14771477
return astIsBool(tok) || isUsedAsBool(tok);
14781478
}
14791479

1480-
bool isBooleanFuncArg(const Token* tok) {
1481-
if (tok->variable() && tok->variable()->valueType() && tok->variable()->valueType()->type == ValueType::BOOL) // skip trivial case: bool passed as bool
1482-
return false;
1483-
int argn{};
1484-
const Token* ftok = getTokenArgumentFunction(tok, argn);
1485-
if (!ftok)
1486-
return false;
1487-
std::vector<const Variable*> argvars = getArgumentVars(ftok, argn);
1488-
if (argvars.size() != 1)
1489-
return false;
1490-
return !argvars[0]->isReference() && argvars[0]->valueType() && argvars[0]->valueType()->type == ValueType::BOOL;
1491-
}
1492-
14931480
bool isSameExpression(bool cpp, bool macro, const Token *tok1, const Token *tok2, const Library& library, bool pure, bool followVar, ErrorPath* errors)
14941481
{
14951482
if (tok1 == nullptr && tok2 == nullptr)

lib/astutils.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -256,11 +256,6 @@ const Token* isInLoopCondition(const Token* tok);
256256
*/
257257
CPPCHECKLIB bool isUsedAsBool(const Token* const tok, const Settings* settings = nullptr);
258258

259-
/**
260-
* Is token passed to a function taking a bool argument
261-
*/
262-
CPPCHECKLIB bool isBooleanFuncArg(const Token* tok);
263-
264259
/**
265260
* Are two conditions opposite
266261
* @param isNot do you want to know if cond1 is !cond2 or if cond1 and cond2 are non-overlapping. true: cond1==!cond2 false: cond1==true => cond2==false

lib/checkfunctions.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -673,7 +673,7 @@ void CheckFunctions::returnLocalStdMove()
673673
const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
674674
for (const Scope *scope : symbolDatabase->functionScopes) {
675675
// Expect return by-value
676-
if (Function::returnsReference(scope->function, true))
676+
if (Function::returnsReference(scope->function, /*unknown*/ true, /*includeRValueRef*/ true))
677677
continue;
678678
const auto rets = Function::findReturns(scope->function);
679679
for (const Token* ret : rets) {

lib/checkstring.cpp

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -288,13 +288,7 @@ void CheckString::checkIncorrectStringCompare()
288288
incorrectStringCompareError(tok->next(), "substr", end->strAt(1));
289289
}
290290
}
291-
} else if (Token::Match(tok, "&&|%oror%|( %str%|%char% &&|%oror%|)") && !Token::Match(tok, "( %str%|%char% )")) {
292-
incorrectStringBooleanError(tok->next(), tok->strAt(1));
293-
} else if (Token::Match(tok, "if|while ( %str%|%char% )") && !tok->tokAt(2)->getValue(0)) {
294-
incorrectStringBooleanError(tok->tokAt(2), tok->strAt(2));
295-
} else if (tok->str() == "?" && Token::Match(tok->astOperand1(), "%str%|%char%")) {
296-
incorrectStringBooleanError(tok->astOperand1(), tok->astOperand1()->str());
297-
} else if (Token::Match(tok, "%str%") && isBooleanFuncArg(tok))
291+
} else if (Token::Match(tok, "%str%|%char%") && isUsedAsBool(tok))
298292
incorrectStringBooleanError(tok, tok->str());
299293
}
300294
}

test/cfg/std.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4503,6 +4503,11 @@ void stdstring()
45034503

45044504
// valid
45054505
s.assign("a");
4506+
4507+
#ifdef __cpp_lib_starts_ends_with
4508+
// cppcheck-suppress ignoredReturnValue
4509+
s.starts_with("abc");
4510+
#endif
45064511
}
45074512

45084513
void stdvector()

test/testfunctions.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1808,6 +1808,12 @@ class TestFunctions : public TestFixture {
18081808
" std::cout << p->msg;\n"
18091809
"}\n");
18101810
ASSERT_EQUALS("", errout.str());
1811+
1812+
check("std::string&& f() {\n" // #11881
1813+
" std::string s;\n"
1814+
" return std::move(s);\n"
1815+
"}\n");
1816+
ASSERT_EQUALS("", errout.str());
18111817
}
18121818

18131819
void negativeMemoryAllocationSizeError() { // #389

test/teststring.cpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -735,7 +735,9 @@ class TestString : public TestFixture {
735735
" if('\\0'){}\n"
736736
" if(L'\\0'){}\n"
737737
"}");
738-
ASSERT_EQUALS("", errout.str());
738+
ASSERT_EQUALS("[test.cpp:2]: (warning) Conversion of char literal '\\0' to bool always evaluates to false.\n"
739+
"[test.cpp:3]: (warning) Conversion of char literal L'\\0' to bool always evaluates to false.\n",
740+
errout.str());
739741

740742
check("void f() {\n"
741743
" if('\\0' || cond){}\n"
@@ -750,6 +752,22 @@ class TestString : public TestFixture {
750752
" f(\"abc\");\n"
751753
"}\n");
752754
ASSERT_EQUALS("[test.cpp:4]: (warning) Conversion of string literal \"abc\" to bool always evaluates to true.\n", errout.str());
755+
756+
check("void g(bool);\n"
757+
" void f(std::map<std::string, std::vector<int>>&m) {\n"
758+
" if (m.count(\"abc\"))\n"
759+
" g(m[\"abc\"][0] ? true : false);\n"
760+
"}\n");
761+
ASSERT_EQUALS("", errout.str());
762+
763+
check("void g(bool b);\n"
764+
"void f() {\n"
765+
" g('\\0');\n"
766+
" g('a');\n"
767+
"}\n");
768+
ASSERT_EQUALS("[test.cpp:3]: (warning) Conversion of char literal '\\0' to bool always evaluates to false.\n"
769+
"[test.cpp:4]: (warning) Conversion of char literal 'a' to bool always evaluates to true.\n",
770+
errout.str());
753771
}
754772

755773
void deadStrcmp() {

0 commit comments

Comments
 (0)