From 725c431eccee6c7bb5c5e165db63410ebf59100a Mon Sep 17 00:00:00 2001
From: chrchr-github <78114321+chrchr-github@users.noreply.github.com>
Date: Mon, 21 Aug 2023 10:43:54 +0200
Subject: [PATCH] Fix #11881 FP returnStdMoveLocal / Fix FP
incorrectStringBooleanError / Support std::string::starts/ends_with() (#5347)
---
cfg/std.cfg | 12 ++++++++++++
lib/astutils.cpp | 13 -------------
lib/astutils.h | 5 -----
lib/checkfunctions.cpp | 2 +-
lib/checkstring.cpp | 8 +-------
test/cfg/std.cpp | 5 +++++
test/testfunctions.cpp | 6 ++++++
test/teststring.cpp | 20 +++++++++++++++++++-
8 files changed, 44 insertions(+), 27 deletions(-)
diff --git a/cfg/std.cfg b/cfg/std.cfg
index 97f5ce015..0acf4a3ce 100644
--- a/cfg/std.cfg
+++ b/cfg/std.cfg
@@ -6884,6 +6884,18 @@ The obsolete function 'gets' is called. With 'gets' you'll get a buffer overrun
+
+
+
+
+ false
+
+
+
+
+
+
+
diff --git a/lib/astutils.cpp b/lib/astutils.cpp
index 868c48f9a..4c7ad9098 100644
--- a/lib/astutils.cpp
+++ b/lib/astutils.cpp
@@ -1477,19 +1477,6 @@ static bool astIsBoolLike(const Token* tok)
return astIsBool(tok) || isUsedAsBool(tok);
}
-bool isBooleanFuncArg(const Token* tok) {
- if (tok->variable() && tok->variable()->valueType() && tok->variable()->valueType()->type == ValueType::BOOL) // skip trivial case: bool passed as bool
- return false;
- int argn{};
- const Token* ftok = getTokenArgumentFunction(tok, argn);
- if (!ftok)
- return false;
- std::vector argvars = getArgumentVars(ftok, argn);
- if (argvars.size() != 1)
- return false;
- return !argvars[0]->isReference() && argvars[0]->valueType() && argvars[0]->valueType()->type == ValueType::BOOL;
-}
-
bool isSameExpression(bool cpp, bool macro, const Token *tok1, const Token *tok2, const Library& library, bool pure, bool followVar, ErrorPath* errors)
{
if (tok1 == nullptr && tok2 == nullptr)
diff --git a/lib/astutils.h b/lib/astutils.h
index af5f6aff6..c34bdd81b 100644
--- a/lib/astutils.h
+++ b/lib/astutils.h
@@ -256,11 +256,6 @@ const Token* isInLoopCondition(const Token* tok);
*/
CPPCHECKLIB bool isUsedAsBool(const Token* const tok, const Settings* settings = nullptr);
-/**
- * Is token passed to a function taking a bool argument
- */
-CPPCHECKLIB bool isBooleanFuncArg(const Token* tok);
-
/**
* Are two conditions opposite
* @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
diff --git a/lib/checkfunctions.cpp b/lib/checkfunctions.cpp
index 36f67d753..2232c2be6 100644
--- a/lib/checkfunctions.cpp
+++ b/lib/checkfunctions.cpp
@@ -673,7 +673,7 @@ void CheckFunctions::returnLocalStdMove()
const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
for (const Scope *scope : symbolDatabase->functionScopes) {
// Expect return by-value
- if (Function::returnsReference(scope->function, true))
+ if (Function::returnsReference(scope->function, /*unknown*/ true, /*includeRValueRef*/ true))
continue;
const auto rets = Function::findReturns(scope->function);
for (const Token* ret : rets) {
diff --git a/lib/checkstring.cpp b/lib/checkstring.cpp
index afdad191c..c9965b53e 100644
--- a/lib/checkstring.cpp
+++ b/lib/checkstring.cpp
@@ -288,13 +288,7 @@ void CheckString::checkIncorrectStringCompare()
incorrectStringCompareError(tok->next(), "substr", end->strAt(1));
}
}
- } else if (Token::Match(tok, "&&|%oror%|( %str%|%char% &&|%oror%|)") && !Token::Match(tok, "( %str%|%char% )")) {
- incorrectStringBooleanError(tok->next(), tok->strAt(1));
- } else if (Token::Match(tok, "if|while ( %str%|%char% )") && !tok->tokAt(2)->getValue(0)) {
- incorrectStringBooleanError(tok->tokAt(2), tok->strAt(2));
- } else if (tok->str() == "?" && Token::Match(tok->astOperand1(), "%str%|%char%")) {
- incorrectStringBooleanError(tok->astOperand1(), tok->astOperand1()->str());
- } else if (Token::Match(tok, "%str%") && isBooleanFuncArg(tok))
+ } else if (Token::Match(tok, "%str%|%char%") && isUsedAsBool(tok))
incorrectStringBooleanError(tok, tok->str());
}
}
diff --git a/test/cfg/std.cpp b/test/cfg/std.cpp
index c6a044125..4952c79d8 100644
--- a/test/cfg/std.cpp
+++ b/test/cfg/std.cpp
@@ -4503,6 +4503,11 @@ void stdstring()
// valid
s.assign("a");
+
+#ifdef __cpp_lib_starts_ends_with
+ // cppcheck-suppress ignoredReturnValue
+ s.starts_with("abc");
+#endif
}
void stdvector()
diff --git a/test/testfunctions.cpp b/test/testfunctions.cpp
index 13589ac13..8eacf8b1c 100644
--- a/test/testfunctions.cpp
+++ b/test/testfunctions.cpp
@@ -1808,6 +1808,12 @@ private:
" std::cout << p->msg;\n"
"}\n");
ASSERT_EQUALS("", errout.str());
+
+ check("std::string&& f() {\n" // #11881
+ " std::string s;\n"
+ " return std::move(s);\n"
+ "}\n");
+ ASSERT_EQUALS("", errout.str());
}
void negativeMemoryAllocationSizeError() { // #389
diff --git a/test/teststring.cpp b/test/teststring.cpp
index 4829c56c8..87ffcccaa 100644
--- a/test/teststring.cpp
+++ b/test/teststring.cpp
@@ -735,7 +735,9 @@ private:
" if('\\0'){}\n"
" if(L'\\0'){}\n"
"}");
- ASSERT_EQUALS("", errout.str());
+ ASSERT_EQUALS("[test.cpp:2]: (warning) Conversion of char literal '\\0' to bool always evaluates to false.\n"
+ "[test.cpp:3]: (warning) Conversion of char literal L'\\0' to bool always evaluates to false.\n",
+ errout.str());
check("void f() {\n"
" if('\\0' || cond){}\n"
@@ -750,6 +752,22 @@ private:
" f(\"abc\");\n"
"}\n");
ASSERT_EQUALS("[test.cpp:4]: (warning) Conversion of string literal \"abc\" to bool always evaluates to true.\n", errout.str());
+
+ check("void g(bool);\n"
+ " void f(std::map>&m) {\n"
+ " if (m.count(\"abc\"))\n"
+ " g(m[\"abc\"][0] ? true : false);\n"
+ "}\n");
+ ASSERT_EQUALS("", errout.str());
+
+ check("void g(bool b);\n"
+ "void f() {\n"
+ " g('\\0');\n"
+ " g('a');\n"
+ "}\n");
+ ASSERT_EQUALS("[test.cpp:3]: (warning) Conversion of char literal '\\0' to bool always evaluates to false.\n"
+ "[test.cpp:4]: (warning) Conversion of char literal 'a' to bool always evaluates to true.\n",
+ errout.str());
}
void deadStrcmp() {