diff --git a/.github/workflows/asan.yml b/.github/workflows/asan.yml
index df2cb0534d4..fdc029ad9f8 100644
--- a/.github/workflows/asan.yml
+++ b/.github/workflows/asan.yml
@@ -39,6 +39,7 @@ jobs:
- name: Install clang
run: |
+ sudo apt-get purge --auto-remove llvm python3-lldb-14 llvm-14
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh 16
diff --git a/.github/workflows/clang-tidy.yml b/.github/workflows/clang-tidy.yml
index 5352c28118a..c8546db9ab2 100644
--- a/.github/workflows/clang-tidy.yml
+++ b/.github/workflows/clang-tidy.yml
@@ -27,6 +27,7 @@ jobs:
- name: Install clang
run: |
+ sudo apt-get purge --auto-remove llvm python3-lldb-14 llvm-14
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh 16
diff --git a/.github/workflows/iwyu.yml b/.github/workflows/iwyu.yml
index 01c705b9da5..a6773e9938e 100644
--- a/.github/workflows/iwyu.yml
+++ b/.github/workflows/iwyu.yml
@@ -107,6 +107,7 @@ jobs:
- name: Install clang
run: |
+ sudo apt-get purge --auto-remove llvm python3-lldb-14 llvm-14
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh 17
diff --git a/.github/workflows/scriptcheck.yml b/.github/workflows/scriptcheck.yml
index 13b87c37ff5..6ef6d90ee4c 100644
--- a/.github/workflows/scriptcheck.yml
+++ b/.github/workflows/scriptcheck.yml
@@ -37,12 +37,11 @@ jobs:
needs: build
# 'ubuntu-22.04' removes Python 2.7, 3.5 and 3.6 so keep the previous LTS version
+ # 'ubutunu-20.04' no longer works on 2.7 - TODO: re-added in a different way or remove support for it?
runs-on: ubuntu-20.04
- container:
- image: python:2.7.18-buster
strategy:
matrix:
- python-version: [2.7, 3.5, 3.6, 3.7, 3.8, 3.9, '3.10', '3.11']
+ python-version: [3.5, 3.6, 3.7, 3.8, 3.9, '3.10', '3.11']
fail-fast: false
steps:
diff --git a/.github/workflows/tsan.yml b/.github/workflows/tsan.yml
index fc3af112481..5a5aaa2c900 100644
--- a/.github/workflows/tsan.yml
+++ b/.github/workflows/tsan.yml
@@ -39,6 +39,7 @@ jobs:
- name: Install clang
run: |
+ sudo apt-get purge --auto-remove llvm python3-lldb-14 llvm-14
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh 16
diff --git a/.github/workflows/ubsan.yml b/.github/workflows/ubsan.yml
index 1de89f66ff5..ed6d4e61f6c 100644
--- a/.github/workflows/ubsan.yml
+++ b/.github/workflows/ubsan.yml
@@ -39,6 +39,7 @@ jobs:
- name: Install clang
run: |
+ sudo apt-get purge --auto-remove llvm python3-lldb-14 llvm-14
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh 16
diff --git a/AUTHORS b/AUTHORS
index e3610324298..4463df0c5ec 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -315,6 +315,7 @@ Ryan Pavlik
Samir Aguiar
Sam Truscott
Samuel Degrande
+Samuel Poláček
Sandeep Dutta
Savvas Etairidis
Scott Furry
diff --git a/addons/misra_9.py b/addons/misra_9.py
index 02940c440f7..f0f73371906 100644
--- a/addons/misra_9.py
+++ b/addons/misra_9.py
@@ -418,7 +418,7 @@ def misra_9_x(self, data, rule, rawTokens = None):
has_var = True
continue
unknown_constant = True
- cppcheckdata.reportError(sz, 'error', f'Unknown constant {t.str}, please review configuration', 'misra', 'config')
+ cppcheckdata.reportError(sz, 'error', 'Unknown constant {}, please review configuration'.format(t.str), 'misra', 'config')
has_config_errors = True
if t.isArithmeticalOp:
tokens += [t.astOperand1, t.astOperand2]
diff --git a/cfg/posix.cfg b/cfg/posix.cfg
index f07f8fe8b44..7cde306a3ad 100644
--- a/cfg/posix.cfg
+++ b/cfg/posix.cfg
@@ -2197,7 +2197,7 @@ The function 'mktemp' is considered to be dangerous due to race conditions and s
-
+
@@ -2209,7 +2209,7 @@ The function 'mktemp' is considered to be dangerous due to race conditions and s
-
+
@@ -2221,7 +2221,7 @@ The function 'mktemp' is considered to be dangerous due to race conditions and s
-
+
diff --git a/cli/cmdlineparser.cpp b/cli/cmdlineparser.cpp
index d76edbf9114..0848f483cd7 100644
--- a/cli/cmdlineparser.cpp
+++ b/cli/cmdlineparser.cpp
@@ -1031,7 +1031,7 @@ bool CmdLineParser::parseFromArgs(int argc, const char* const argv[])
else if ((def || mSettings.preprocessOnly) && !maxconfigs)
mSettings.maxConfigs = 1U;
- if (mSettings.checks.isEnabled(Checks::unusedFunction) && mSettings.jobs > 1) {
+ if (mSettings.checks.isEnabled(Checks::unusedFunction) && mSettings.jobs > 1 && mSettings.buildDir.empty()) {
printMessage("unusedFunction check can't be used with '-j' option. Disabling unusedFunction check.");
}
diff --git a/lib/astutils.cpp b/lib/astutils.cpp
index 3652eefbe0d..94307ab276f 100644
--- a/lib/astutils.cpp
+++ b/lib/astutils.cpp
@@ -2196,7 +2196,7 @@ T* getTokenArgumentFunctionImpl(T* tok, int& argn)
parent = parent->astParent();
// passing variable to subfunction?
- if (Token::Match(parent, "[(,{]"))
+ if (Token::Match(parent, "[[(,{]"))
;
else if (Token::simpleMatch(parent, ":")) {
while (Token::Match(parent, "[?:]"))
diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp
index abf8319241c..934d237b7a8 100644
--- a/lib/checkclass.cpp
+++ b/lib/checkclass.cpp
@@ -1067,7 +1067,7 @@ void CheckClass::noConstructorError(const Token *tok, const std::string &classna
void CheckClass::noExplicitConstructorError(const Token *tok, const std::string &classname, bool isStruct)
{
const std::string message(std::string(isStruct ? "Struct" : "Class") + " '$symbol' has a constructor with 1 argument that is not explicit.");
- const std::string verbose(message + " Such constructors should in general be explicit for type safety reasons. Using the explicit keyword in the constructor means some mistakes when using the class can be avoided.");
+ const std::string verbose(message + " Such, so called \"Converting constructors\", should in general be explicit for type safety reasons as that prevents unintended implicit conversions.");
reportError(tok, Severity::style, "noExplicitConstructor", "$symbol:" + classname + '\n' + message + '\n' + verbose, CWE398, Certainty::normal);
}
@@ -2582,7 +2582,7 @@ void CheckClass::checkConstError2(const Token *tok1, const Token *tok2, const st
"passed to the function. This change should not cause compiler errors but it does not "
"necessarily make sense conceptually. Think about your design and the task of the function first - "
"is it a function that must not access members of class instances? And maybe it is more appropriate "
- "to move this function to a unnamed namespace.", CWE398, Certainty::inconclusive);
+ "to move this function to an unnamed namespace.", CWE398, Certainty::inconclusive);
}
//---------------------------------------------------------------------------
diff --git a/lib/checkother.cpp b/lib/checkother.cpp
index 2a167a068aa..a0d2677ad58 100644
--- a/lib/checkother.cpp
+++ b/lib/checkother.cpp
@@ -991,7 +991,7 @@ void CheckOther::checkVariableScope()
}
}
-bool CheckOther::checkInnerScope(const Token *tok, const Variable* var, bool& used)
+bool CheckOther::checkInnerScope(const Token *tok, const Variable* var, bool& used) const
{
const Scope* scope = tok->next()->scope();
bool loopVariable = scope->isLoopScope();
@@ -1071,6 +1071,18 @@ bool CheckOther::checkInnerScope(const Token *tok, const Variable* var, bool& us
if (scope->bodyStart && scope->bodyStart->isSimplifiedScope())
return false; // simplified if/for/switch init statement
}
+ if (var->isArrayOrPointer()) {
+ int argn{};
+ if (const Token* ftok = getTokenArgumentFunction(tok, argn)) { // var passed to function?
+ if (ftok->next()->astParent()) { // return value used?
+ if (ftok->function() && Function::returnsPointer(ftok->function()))
+ return false;
+ const std::string ret = mSettings->library.returnValueType(ftok); // assume that var is returned
+ if (!ret.empty() && ret.back() == '*')
+ return false;
+ }
+ }
+ }
}
}
diff --git a/lib/checkother.h b/lib/checkother.h
index 14e0409145e..747f1e63857 100644
--- a/lib/checkother.h
+++ b/lib/checkother.h
@@ -123,7 +123,7 @@ class CPPCHECKLIB CheckOther : public Check {
/** @brief %Check scope of variables */
void checkVariableScope();
- static bool checkInnerScope(const Token *tok, const Variable* var, bool& used);
+ bool checkInnerScope(const Token *tok, const Variable* var, bool& used) const;
/** @brief %Check for comma separated statements in return */
void checkCommaSeparatedReturn();
diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp
index 41d99055f45..99e3f48153d 100644
--- a/lib/symboldatabase.cpp
+++ b/lib/symboldatabase.cpp
@@ -1107,7 +1107,7 @@ void SymbolDatabase::createSymbolDatabaseSetFunctionPointers(bool firstPass)
continue;
bool isTemplateArg = false;
- if (tok->next()->str() != "(") {
+ if (!Token::Match(tok->next(), "(|{")) {
const Token *start = tok;
while (Token::Match(start->tokAt(-2), "%name% ::"))
start = start->tokAt(-2);
@@ -3490,16 +3490,28 @@ const Token *Type::initBaseInfo(const Token *tok, const Token *tok1)
return tok2;
}
-const std::string& Type::name() const
+std::string Type::name() const
{
- const Token* next = classDef->next();
+ const Token* start = classDef->next();
if (classScope && classScope->enumClass && isEnumType())
- return next->strAt(1);
- if (next->str() == "class")
- return next->strAt(1);
- if (next->isName())
- return next->str();
- return emptyString;
+ start = start->tokAt(1);
+ else if (start->str() == "class")
+ start = start->tokAt(1);
+ else if (!start->isName())
+ return emptyString;
+ const Token* next = start;
+ while (Token::Match(next, "::|<|>|(|)|[|]|*|&|&&|%name%")) {
+ if (Token::Match(next, "<|(|[") && next->link())
+ next = next->link();
+ next = next->next();
+ }
+ std::string result;
+ for (const Token* tok = start; tok != next; tok = tok->next()) {
+ if (!result.empty())
+ result += ' ';
+ result += tok->str();
+ }
+ return result;
}
void SymbolDatabase::debugMessage(const Token *tok, const std::string &type, const std::string &msg) const
diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h
index ffd3217634e..aa7d644a69a 100644
--- a/lib/symboldatabase.h
+++ b/lib/symboldatabase.h
@@ -131,7 +131,7 @@ class CPPCHECKLIB Type {
}
}
- const std::string& name() const;
+ std::string name() const;
const std::string& type() const {
return classDef ? classDef->str() : emptyString;
diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp
index a0fd9e5fc87..d0ef72a758d 100644
--- a/lib/tokenize.cpp
+++ b/lib/tokenize.cpp
@@ -4759,7 +4759,8 @@ void Tokenizer::setVarIdPass1()
continue;
}
- if (!scopeStack.top().isEnum || !(Token::Match(tok->previous(), "{|,") && Token::Match(tok->next(), ",|=|}"))) {
+ if ((!scopeStack.top().isEnum || !(Token::Match(tok->previous(), "{|,") && Token::Match(tok->next(), ",|=|}"))) &&
+ !Token::simpleMatch(tok->next(), ": ;")) {
const std::map::const_iterator it = variableMap.map(globalNamespace).find(tok->str());
if (it != variableMap.map(globalNamespace).end()) {
tok->varId(it->second);
@@ -5251,7 +5252,8 @@ void Tokenizer::createLinks2()
} else if (token->str() == "<" &&
((token->previous() && (token->previous()->isTemplate() ||
(token->previous()->isName() && !token->previous()->varId()) ||
- (token->strAt(-1) == "]" && (!Token::Match(token->linkAt(-1)->previous(), "%name%|)") || token->linkAt(-1)->previous()->isKeyword())))) ||
+ (token->strAt(-1) == "]" && (!Token::Match(token->linkAt(-1)->previous(), "%name%|)") || token->linkAt(-1)->previous()->isKeyword())) ||
+ (token->strAt(-1) == ")" && token->linkAt(-1)->strAt(-1) == "operator"))) ||
Token::Match(token->next(), ">|>>"))) {
type.push(token);
if (token->previous()->str() == "template")
diff --git a/lib/tokenlist.cpp b/lib/tokenlist.cpp
index b4af023ba0d..6a8a5edc858 100644
--- a/lib/tokenlist.cpp
+++ b/lib/tokenlist.cpp
@@ -1747,7 +1747,7 @@ void TokenList::validateAst() const
continue;
}
- if (const Token* lambdaEnd = findLambdaEndToken(tok)) { // skip lambda captures
+ if (findLambdaEndToken(tok)) { // skip lambda captures
tok = tok->link();
continue;
}
diff --git a/test/cfg/posix.c b/test/cfg/posix.c
index 3da4959536a..0621e983618 100644
--- a/test/cfg/posix.c
+++ b/test/cfg/posix.c
@@ -330,6 +330,13 @@ double nullPointer_erand48(unsigned short xsubi[3])
return erand48(xsubi);
}
+struct non_const_parameter_erand48_struct { unsigned short xsubi[3]; };
+// No warning is expected that dat can be const
+double non_const_parameter_erand48(struct non_const_parameter_erand48_struct *dat)
+{
+ return erand48(dat->xsubi);
+}
+
unsigned short *nullPointer_seed48(unsigned short seed16v[3])
{
// cppcheck-suppress nullPointer
diff --git a/test/testclass.cpp b/test/testclass.cpp
index 53371d545c3..f79744666c7 100644
--- a/test/testclass.cpp
+++ b/test/testclass.cpp
@@ -6061,7 +6061,7 @@ class TestClass : public TestFixture {
" int i{};\n"
" S f() { return S{ &i }; }\n"
"};\n");
- TODO_ASSERT_EQUALS("[test.cpp:7]: (style, inconclusive) Technically the member function 'C::f' can be const.\n", "", errout.str());
+ ASSERT_EQUALS("[test.cpp:7]: (style, inconclusive) Technically the member function 'C::f' can be const.\n", errout.str());
checkConst("struct S {\n"
" explicit S(const int* p) : mp(p) {}\n"
diff --git a/test/testother.cpp b/test/testother.cpp
index 91d1beef558..2024a031f1d 100644
--- a/test/testother.cpp
+++ b/test/testother.cpp
@@ -106,6 +106,7 @@ class TestOther : public TestFixture {
TEST_CASE(varScope32); // #11441
TEST_CASE(varScope33);
TEST_CASE(varScope34);
+ TEST_CASE(varScope35);
TEST_CASE(oldStylePointerCast);
TEST_CASE(invalidPointerCast);
@@ -1636,6 +1637,37 @@ class TestOther : public TestFixture {
ASSERT_EQUALS("", errout.str());
}
+ void varScope35() { // #11845
+ check("void f(int err, const char* src) {\n"
+ " const char* msg = \"Success\";\n"
+ " char buf[42];\n"
+ " if (err != 0)\n"
+ " msg = strcpy(buf, src);\n"
+ " printf(\"%d: %s\\n\", err, msg);\n"
+ "}\n");
+ ASSERT_EQUALS("", errout.str());
+
+ check("char* g(char* dst, const char* src);\n"
+ "void f(int err, const char* src) {\n"
+ " const char* msg = \"Success\";\n"
+ " char buf[42];\n"
+ " if (err != 0)\n"
+ " msg = g(buf, src);\n"
+ " printf(\"%d: %s\\n\", err, msg);\n"
+ "}\n");
+ ASSERT_EQUALS("", errout.str());
+
+ check("char* g(char* dst, const char* src);\n"
+ "void f(int err, const char* src) {\n"
+ " const char* msg = \"Success\";\n"
+ " char buf[42];\n"
+ " if (err != 0)\n"
+ " g(buf, src);\n"
+ " printf(\"%d: %s\\n\", err, msg);\n"
+ "}\n");
+ ASSERT_EQUALS("[test.cpp:4]: (style) The scope of the variable 'buf' can be reduced.\n", errout.str());
+ }
+
#define checkOldStylePointerCast(code) checkOldStylePointerCast_(code, __FILE__, __LINE__)
void checkOldStylePointerCast_(const char code[], const char* file, int line) {
// Clear the error buffer..
@@ -3754,6 +3786,13 @@ class TestOther : public TestFixture {
" p = q;\n"
"}\n");
ASSERT_EQUALS("", errout.str());
+
+ check("struct S { int a[1]; };\n"
+ "void f(S* s) {\n"
+ " if (s->a[0]) {}\n"
+ "}\n");
+ ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 's' can be declared as pointer to const\n",
+ errout.str());
}
void switchRedundantAssignmentTest() {
diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp
index fc8558bc921..2e0972d07b8 100644
--- a/test/testtokenize.cpp
+++ b/test/testtokenize.cpp
@@ -3531,6 +3531,25 @@ class TestTokenizer : public TestFixture {
ASSERT_EQUALS(true, tok1->link() == tok2);
ASSERT_EQUALS(true, tok2->link() == tok1);
}
+
+ {
+ const char code[] = "struct S {\n" // #11840
+ " template\n"
+ " void operator() (int);\n"
+ "};\n"
+ "void f() {\n"
+ " S s;\n"
+ " s.operator()(1);\n"
+ "}\n";
+ errout.str("");
+ Tokenizer tokenizer(&settings0, this);
+ std::istringstream istr(code);
+ ASSERT(tokenizer.tokenize(istr, "test.cpp"));
+ const Token* tok1 = Token::findsimplematch(tokenizer.tokens(), "< int");
+ const Token* tok2 = Token::findsimplematch(tok1, "> (");
+ ASSERT_EQUALS(true, tok1->link() == tok2);
+ ASSERT_EQUALS(true, tok2->link() == tok1);
+ }
}
void simplifyString() {
diff --git a/test/testvalueflow.cpp b/test/testvalueflow.cpp
index 2592eeec69a..4e322596175 100644
--- a/test/testvalueflow.cpp
+++ b/test/testvalueflow.cpp
@@ -6812,6 +6812,14 @@ class TestValueFlow : public TestFixture {
" dummy_resource::log.clear();\n"
"}\n";
valueOfTok(code, "log");
+
+ code = "struct D : B {\n"
+ " D(int i, const std::string& s) : B(i, s) {}\n"
+ "};\n"
+ "template<> struct B::S {\n"
+ " int j;\n"
+ "};\n";
+ valueOfTok(code, "B");
}
void valueFlowCrash() {
diff --git a/test/testvarid.cpp b/test/testvarid.cpp
index e0ffc150020..90dfeafe604 100644
--- a/test/testvarid.cpp
+++ b/test/testvarid.cpp
@@ -2785,6 +2785,17 @@ class TestVarID : public TestFixture {
" break;\n"
" }\n"
"}", "test.c"));
+
+ ASSERT_EQUALS("1: int * f ( ) {\n" // #11838
+ "2: int * label@1 ; label@1 = 0 ;\n"
+ "3: label : ;\n"
+ "4: return label@1 ;\n"
+ "5: }\n",
+ tokenize("int* f() {\n"
+ " int* label = 0;\n"
+ "label:\n"
+ " return label;\n"
+ "}"));
}
void varid_structinit() { // #6406