diff --git a/.github/workflows/CI-unixish-docker.yml b/.github/workflows/CI-unixish-docker.yml index 82439c9bbe4..ec392f9da8b 100644 --- a/.github/workflows/CI-unixish-docker.yml +++ b/.github/workflows/CI-unixish-docker.yml @@ -19,10 +19,10 @@ jobs: strategy: matrix: - image: ["ubuntu:16.04", "ubuntu:18.04", "ubuntu:23.10"] + image: ["ubuntu:16.04", "ubuntu:18.04", "ubuntu:24.04"] include: - build_gui: false - - image: "ubuntu:23.10" + - image: "ubuntu:24.04" build_gui: true fail-fast: false # Prefer quick result @@ -92,7 +92,7 @@ jobs: strategy: matrix: - image: ["ubuntu:16.04", "ubuntu:18.04", "ubuntu:23.10"] + image: ["ubuntu:16.04", "ubuntu:18.04", "ubuntu:24.04"] fail-fast: false # Prefer quick result runs-on: ubuntu-22.04 diff --git a/Makefile b/Makefile index 1deec1f8225..6dc456220e3 100644 --- a/Makefile +++ b/Makefile @@ -484,7 +484,7 @@ $(libcppdir)/check.o: lib/check.cpp lib/addoninfo.h lib/check.h lib/color.h lib/ $(libcppdir)/check64bit.o: lib/check64bit.cpp lib/addoninfo.h lib/check.h lib/check64bit.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/check64bit.cpp -$(libcppdir)/checkassert.o: lib/checkassert.cpp lib/addoninfo.h lib/check.h lib/checkassert.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h +$(libcppdir)/checkassert.o: lib/checkassert.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkassert.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkassert.cpp $(libcppdir)/checkautovariables.o: lib/checkautovariables.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkautovariables.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/vfvalue.h diff --git a/cfg/gtk.cfg b/cfg/gtk.cfg index fafb34665b2..1e20a335128 100644 --- a/cfg/gtk.cfg +++ b/cfg/gtk.cfg @@ -4112,6 +4112,7 @@ + false diff --git a/cfg/kde.cfg b/cfg/kde.cfg index 5973916fd60..7d18998a29f 100644 --- a/cfg/kde.cfg +++ b/cfg/kde.cfg @@ -48,4 +48,40 @@ + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + diff --git a/lib/checkassert.cpp b/lib/checkassert.cpp index a96191288c2..0202ed7454a 100644 --- a/lib/checkassert.cpp +++ b/lib/checkassert.cpp @@ -22,6 +22,7 @@ #include "checkassert.h" +#include "astutils.h" #include "errortypes.h" #include "settings.h" #include "symboldatabase.h" @@ -57,8 +58,20 @@ void CheckAssert::assertWithSideEffects() checkVariableAssignment(tmp, tok->scope()); - if (tmp->tokType() != Token::eFunction) + if (tmp->tokType() != Token::eFunction) { + if (const Library::Function* f = mSettings->library.getFunction(tmp)) { + if (f->isconst || f->ispure) + continue; + if (Library::getContainerYield(tmp->next()) != Library::Container::Yield::NO_YIELD) // bailout, assume read access + continue; + if (std::any_of(f->argumentChecks.begin(), f->argumentChecks.end(), [](const std::pair& ac) { + return ac.second.iteratorInfo.container > 0; // bailout, takes iterators -> assume read access + })) + continue; + sideEffectInAssertError(tmp, mSettings->library.getFunctionName(tmp)); + } continue; + } const Function* f = tmp->function(); const Scope* scope = f->functionScope; diff --git a/lib/library.cpp b/lib/library.cpp index d449cc55933..ddd2a13ed90 100644 --- a/lib/library.cpp +++ b/lib/library.cpp @@ -1266,6 +1266,22 @@ bool Library::isContainerYield(const Token * const cond, Library::Container::Yie return false; } +Library::Container::Yield Library::getContainerYield(const Token* const cond) +{ + if (Token::simpleMatch(cond, "(")) { + const Token* tok = cond->astOperand1(); + if (tok && tok->str() == ".") { + if (tok->astOperand1() && tok->astOperand1()->valueType()) { + if (const Library::Container *container = tok->astOperand1()->valueType()->container) { + if (tok->astOperand2()) + return container->getYield(tok->astOperand2()->str()); + } + } + } + } + return Library::Container::Yield::NO_YIELD; +} + // returns true if ftok is not a library function bool Library::isNotLibraryFunction(const Token *ftok) const { diff --git a/lib/library.h b/lib/library.h index 31407cbe882..fe6ed3fc1bd 100644 --- a/lib/library.h +++ b/lib/library.h @@ -413,6 +413,9 @@ class CPPCHECKLIB Library { const Token* getContainerFromYield(const Token* tok, Container::Yield yield) const; const Token* getContainerFromAction(const Token* tok, Container::Action action) const; + static bool isContainerYield(const Token* const cond, Library::Container::Yield y, const std::string& fallback = emptyString); + static Library::Container::Yield getContainerYield(const Token* const cond); + bool isreflection(const std::string &token) const { return mReflection.find(token) != mReflection.end(); } @@ -496,8 +499,6 @@ class CPPCHECKLIB Library { */ std::string getFunctionName(const Token *ftok) const; - static bool isContainerYield(const Token * const cond, Library::Container::Yield y, const std::string& fallback=emptyString); - /** Suppress/check a type */ enum class TypeCheck { def, check, diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 7e97729ba9b..a5c48907412 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -8674,8 +8674,6 @@ void Tokenizer::findGarbageCode() const if (Token::Match(tok->next(), ")|]|>|%assign%|%or%|%oror%|==|!=|/|>=|<=|&&")) syntaxError(tok); } - if ((!isCPP() || !Token::simpleMatch(tok->previous(), "operator")) && Token::Match(tok, "[,;] ,")) - syntaxError(tok); if (Token::simpleMatch(tok, ".") && !Token::simpleMatch(tok->previous(), ".") && !Token::simpleMatch(tok->next(), ".") && @@ -8703,15 +8701,19 @@ void Tokenizer::findGarbageCode() const syntaxError(tok); if (Token::Match(tok, "! %comp%")) syntaxError(tok); - if (Token::Match(tok, "] %name%") && (!isCPP() || !(tok->tokAt(-1) && Token::simpleMatch(tok->tokAt(-2), "delete [")))) - syntaxError(tok); + if (Token::Match(tok, "] %name%") && (!isCPP() || !(tok->tokAt(-1) && Token::simpleMatch(tok->tokAt(-2), "delete [")))) { + if (tok->next()->isUpperCaseName()) + unknownMacroError(tok->next()); + else + syntaxError(tok); + } if (tok->link() && Token::Match(tok, "[([]") && (!tok->tokAt(-1) || !tok->tokAt(-1)->isControlFlowKeyword())) { const Token* const end = tok->link(); for (const Token* inner = tok->next(); inner != end; inner = inner->next()) { if (inner->str() == "{") inner = inner->link(); - else if (inner->str() == ";") { + else if (inner->str() == ";" || (Token::simpleMatch(inner, ", ,") && (!isCPP() || !Token::simpleMatch(inner->previous(), "operator")))) { if (tok->tokAt(-1) && tok->tokAt(-1)->isUpperCaseName()) unknownMacroError(tok->tokAt(-1)); else @@ -8719,6 +8721,9 @@ void Tokenizer::findGarbageCode() const } } } + + if ((!isCPP() || !Token::simpleMatch(tok->previous(), "operator")) && Token::Match(tok, "[,;] ,")) + syntaxError(tok); if (tok->str() == "typedef") { for (const Token* tok2 = tok->next(); tok2 && tok2->str() != ";"; tok2 = tok2->next()) { if (!tok2->next() || tok2->isControlFlowKeyword() || Token::Match(tok2, "typedef|static|.")) diff --git a/oss-fuzz/Makefile b/oss-fuzz/Makefile index ba8bb2e7edf..44718ef91bf 100644 --- a/oss-fuzz/Makefile +++ b/oss-fuzz/Makefile @@ -163,7 +163,7 @@ $(libcppdir)/check.o: ../lib/check.cpp ../lib/addoninfo.h ../lib/check.h ../lib/ $(libcppdir)/check64bit.o: ../lib/check64bit.cpp ../lib/addoninfo.h ../lib/check.h ../lib/check64bit.h ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/settings.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/vfvalue.h $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/check64bit.cpp -$(libcppdir)/checkassert.o: ../lib/checkassert.cpp ../lib/addoninfo.h ../lib/check.h ../lib/checkassert.h ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/settings.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/vfvalue.h +$(libcppdir)/checkassert.o: ../lib/checkassert.cpp ../lib/addoninfo.h ../lib/astutils.h ../lib/check.h ../lib/checkassert.h ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/vfvalue.h $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkassert.cpp $(libcppdir)/checkautovariables.o: ../lib/checkautovariables.cpp ../lib/addoninfo.h ../lib/astutils.h ../lib/check.h ../lib/checkautovariables.h ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/valueflow.h ../lib/vfvalue.h diff --git a/test/cfg/gtk.c b/test/cfg/gtk.c index 6d8b2258a91..9c697dda76c 100644 --- a/test/cfg/gtk.c +++ b/test/cfg/gtk.c @@ -68,6 +68,7 @@ void validCode(int argInt, GHashTableIter * hash_table_iter, GHashTable * hash_t // NULL is handled graciously char* str = g_strdup(NULL); if (g_strcmp0(str, NULL) || g_strcmp0(NULL, str)) + // cppcheck-suppress valueFlowBailout // TODO: caused by ? printf("%s", str); g_free(str); } diff --git a/test/cfg/kde.cpp b/test/cfg/kde.cpp index 9a2720949eb..6eb9cfe1bc1 100644 --- a/test/cfg/kde.cpp +++ b/test/cfg/kde.cpp @@ -10,6 +10,7 @@ #include #include #include +#include class k_global_static_testclass1 {}; K_GLOBAL_STATIC(k_global_static_testclass1, k_global_static_testinstance1); @@ -30,3 +31,13 @@ void ignoredReturnValue(const KConfigGroup& cfgGroup) // cppcheck-suppress ignoredReturnValue cfgGroup.readEntry("test"); } + +void i18n_test() +{ + (void)i18n("Text"); + (void)xi18n("Text"); + (void)ki18n("Text"); + (void)i18nc("Text", "Context"); + (void)xi18nc("Text", "Context"); + (void)ki18nc("Text", "Context"); +} diff --git a/test/cfg/std.cpp b/test/cfg/std.cpp index 11efa81f5d3..9ce1a976a68 100644 --- a/test/cfg/std.cpp +++ b/test/cfg/std.cpp @@ -5016,3 +5016,9 @@ void eraseIteratorOutOfBounds_std_deque(std::deque& x) // #8690 // cppcheck-suppress eraseIteratorOutOfBounds x.erase(x.end()); } + +void assertWithSideEffect_system() +{ + // cppcheck-suppress [assertWithSideEffect,checkLibraryNoReturn] // TODO: #8329 + assert(std::system("abc")); +} diff --git a/test/testassert.cpp b/test/testassert.cpp index e8bb9e6a1d5..af77d8d4d55 100644 --- a/test/testassert.cpp +++ b/test/testassert.cpp @@ -134,6 +134,15 @@ class TestAssert : public TestFixture { " assert(empty());\n" "}"); ASSERT_EQUALS("", errout_str()); + + check("struct S {\n" // #4811 + " void f() const;\n" + " bool g(std::ostream& os = std::cerr) const;\n" + "};\n" + "void S::f() const {\n" + " assert(g());\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); } void memberFunctionCallInAssert() { diff --git a/test/testgarbage.cpp b/test/testgarbage.cpp index b58bb1415a4..1850d594345 100644 --- a/test/testgarbage.cpp +++ b/test/testgarbage.cpp @@ -1213,7 +1213,7 @@ class TestGarbage : public TestFixture { " typedef S0 b[][1][1] != 0\n" "};\n" "b[K][0] S0 b[][1][1] != 4{ 0 };\n" - "b[0][0]"), SYNTAX); + "b[0][0]"), UNKNOWN_MACRO); } void garbageCode149() { // #7085 diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index eb1a6b01b41..b07114d7f79 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -7133,6 +7133,14 @@ class TestTokenizer : public TestFixture { InternalError, "There is an unknown macro here somewhere. Configuration is required. If MACRO is a macro then please configure it."); + ASSERT_THROW_EQUALS(tokenizeAndStringify("struct S { int a[2] PACKED; };\n"), + InternalError, + "There is an unknown macro here somewhere. Configuration is required. If PACKED is a macro then please configure it."); + + ASSERT_THROW_EQUALS(tokenizeAndStringify("MACRO(a, b,,)\n"), + InternalError, + "There is an unknown macro here somewhere. Configuration is required. If MACRO is a macro then please configure it."); + ASSERT_THROW_INTERNAL(tokenizeAndStringify("{ for (()()) }"), SYNTAX); // #11643 ASSERT_NO_THROW(tokenizeAndStringify("S* g = ::new(ptr) S();")); // #12552 diff --git a/tools/donate-cpu-server.py b/tools/donate-cpu-server.py index 01323f6d8bd..456f6c21f76 100755 --- a/tools/donate-cpu-server.py +++ b/tools/donate-cpu-server.py @@ -26,7 +26,7 @@ # Version scheme (MAJOR.MINOR.PATCH) should orientate on "Semantic Versioning" https://semver.org/ # Every change in this script should result in increasing the version number accordingly (exceptions may be cosmetic # changes) -SERVER_VERSION = "1.3.50" +SERVER_VERSION = "1.3.52" OLD_VERSION = '2.14.0' @@ -1085,7 +1085,7 @@ class HttpClientThread(Thread): def __init__(self, connection: socket.socket, cmd: str, resultPath: str, latestResults: list) -> None: Thread.__init__(self) self.connection = connection - self.cmd = cmd[:cmd.find('\r\n')] + self.cmd = cmd self.resultPath = resultPath self.infoPath = os.path.join(self.resultPath, 'info_output') self.latestResults = latestResults @@ -1102,12 +1102,12 @@ def parse_req(cmd): def run(self): try: cmd = self.cmd - print_ts(cmd) url, queryParams = self.parse_req(cmd) if url is None: print_ts('invalid request: {}'.format(cmd)) self.connection.close() return + t_start = time.perf_counter() if url == '/': html = overviewReport() httpGetResponse(self.connection, html, 'text/html') @@ -1204,6 +1204,7 @@ def run(self): with open(filename, 'rt') as f: data = f.read() httpGetResponse(self.connection, data, 'text/plain') + print_ts('{} finished in {}s'.format(url, (time.perf_counter() - t_start))) except: tb = "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2])) print_ts(tb) @@ -1221,7 +1222,7 @@ def read_data(connection, cmd, pos_nl, max_data_size, check_done, cmd_name, time bytes_received = connection.recv(1024) if bytes_received: try: - text_received = bytes_received.decode('utf-8', 'ignore') + text_received = bytes_received.decode('ascii', 'ignore') except UnicodeDecodeError as e: print_ts('Error: Decoding failed ({}): {}'.format(cmd_name, e)) data = None @@ -1243,7 +1244,7 @@ def read_data(connection, cmd, pos_nl, max_data_size, check_done, cmd_name, time print_ts('Timeout occurred ({}).'.format(cmd_name)) data = None - if data and (len(data) >= (max_data_size + 1024)): + if data and (len(data) >= max_data_size): print_ts('Maximum allowed data ({} bytes) exceeded ({}).'.format(max_data_size, cmd_name)) data = None @@ -1287,7 +1288,7 @@ def server(server_address_port: int, packages: list, packageIndex: int, resultPa continue pos_nl = cmd.find('\n') if pos_nl < 1: - print_ts('No newline found in data.') + print_ts("No newline found in data: '{}'".format(cmd)) connection.close() continue firstLine = cmd[:pos_nl] @@ -1296,8 +1297,11 @@ def server(server_address_port: int, packages: list, packageIndex: int, resultPa connection.close() continue if cmd.startswith('GET /'): + cmd = cmd[:cmd.find('\r\n')] + print_ts(cmd) newThread = HttpClientThread(connection, cmd, resultPath, latestResults) newThread.start() + continue elif cmd == 'GetCppcheckVersions\n': reply = 'head ' + OLD_VERSION print_ts('GetCppcheckVersions: ' + reply) @@ -1321,7 +1325,8 @@ def server(server_address_port: int, packages: list, packageIndex: int, resultPa connection.close() continue elif cmd.startswith('write\nftp://') or cmd.startswith('write\nhttp://'): - data = read_data(connection, cmd, pos_nl, max_data_size=2 * 1024 * 1024, check_done=True, cmd_name='write') + t_start = time.perf_counter() + data = read_data(connection, cmd, pos_nl, max_data_size=2.5 * 1024 * 1024, check_done=True, cmd_name='write') if data is None: continue @@ -1362,7 +1367,6 @@ def server(server_address_port: int, packages: list, packageIndex: int, resultPa if old_version_wrong: print_ts('Unexpected old version. Ignoring result data.') continue - print_ts('results added for package ' + res.group(1) + ' (' + str(len(data)) + ' bytes)') filename = os.path.join(resultPath, res.group(1)) with open(filename, 'wt') as f: f.write(strDateTime() + '\n' + data) @@ -1374,9 +1378,11 @@ def server(server_address_port: int, packages: list, packageIndex: int, resultPa f.write(' '.join(latestResults)) # generate package.diff.. generate_package_diff_statistics(filename) + print_ts('write finished for {} ({} bytes / {}s)'.format(res.group(1), len(data), (time.perf_counter() - t_start))) continue elif cmd.startswith('write_info\nftp://') or cmd.startswith('write_info\nhttp://'): - data = read_data(connection, cmd, pos_nl, max_data_size=1024 * 1024, check_done=True, cmd_name='write_info') + t_start = time.perf_counter() + data = read_data(connection, cmd, pos_nl, max_data_size=7 * 1024 * 1024, check_done=True, cmd_name='write_info') if data is None: continue @@ -1400,13 +1406,13 @@ def server(server_address_port: int, packages: list, packageIndex: int, resultPa if url not in packages: print_ts('Url is not in packages. Ignoring information data.') continue - print_ts('adding info output for package ' + res.group(1) + ' (' + str(len(data)) + ' bytes)') info_path = resultPath + '/' + 'info_output' if not os.path.exists(info_path): os.mkdir(info_path) filename = info_path + '/' + res.group(1) with open(filename, 'wt') as f: f.write(strDateTime() + '\n' + data) + print_ts('write_info finished for {} ({} bytes / {}s)'.format(res.group(1), len(data), (time.perf_counter() - t_start))) continue elif cmd == 'getPackagesCount\n': packages_count = str(len(packages)) @@ -1421,7 +1427,7 @@ def server(server_address_port: int, packages: list, packageIndex: int, resultPa connection.send(pkg.encode('utf-8', 'ignore')) print_ts('getPackageIdx: ' + pkg) else: - print_ts('getPackageIdx: index is out of range') + print_ts('getPackageIdx: index {} is out of range'.format(request_idx)) connection.close() continue elif cmd.startswith('write_nodata\nftp://'): diff --git a/tools/donate_cpu_lib.py b/tools/donate_cpu_lib.py index 338e106b578..a2a2aaec994 100644 --- a/tools/donate_cpu_lib.py +++ b/tools/donate_cpu_lib.py @@ -16,7 +16,7 @@ # Version scheme (MAJOR.MINOR.PATCH) should orientate on "Semantic Versioning" https://semver.org/ # Every change in this script should result in increasing the version number accordingly (exceptions may be cosmetic # changes) -CLIENT_VERSION = "1.3.57" +CLIENT_VERSION = "1.3.59" # Timeout for analysis with Cppcheck in seconds CPPCHECK_TIMEOUT = 30 * 60 @@ -685,7 +685,7 @@ def __init__(self): 'ginac': [''], 'gtk': ['', '', '', '', '', '', ''], 'libcerror': [''], 'libcurl': [''], 'libsigc++': ['