diff --git a/lib/preprocessor.cpp b/lib/preprocessor.cpp index 0a0acecab1a..c14eaf813ad 100644 --- a/lib/preprocessor.cpp +++ b/lib/preprocessor.cpp @@ -1114,22 +1114,29 @@ void Preprocessor::dump(std::ostream &out) const std::size_t Preprocessor::calculateHash(const std::string &toolinfo) const { std::string hashData = toolinfo; - for (const simplecpp::Token *tok = mTokens.cfront(); tok; tok = tok->next) { - if (!tok->comment) { - hashData += tok->str(); - hashData += static_cast(tok->location.line); - hashData += static_cast(tok->location.col); - } - } - for (const auto &filedata : mFileCache) { - for (const simplecpp::Token *tok = filedata->tokens.cfront(); tok; tok = tok->next) { + const auto addTokens = [&hashData](const simplecpp::TokenList &tokens) { + for (const simplecpp::Token *tok = tokens.cfront(); tok; tok = tok->next) { if (!tok->comment) { hashData += tok->str(); hashData += static_cast(tok->location.line); hashData += static_cast(tok->location.col); } } - } + }; + addTokens(mTokens); + + // Hash the included files in a fixed order, the order they are loaded in differs between runs + std::vector fileData; + fileData.reserve(mFileCache.size()); + std::transform(mFileCache.cbegin(), mFileCache.cend(), std::back_inserter(fileData), [](const std::unique_ptr &data) { + return data.get(); + }); + std::sort(fileData.begin(), fileData.end(), [](const simplecpp::FileData *a, const simplecpp::FileData *b) { + return a->filename < b->filename; + }); + for (const simplecpp::FileData *data : fileData) + addTokens(data->tokens); + return (std::hash{})(hashData); } diff --git a/test/cli/other_test.py b/test/cli/other_test.py index aad4fec264e..c86d8cd5a81 100644 --- a/test/cli/other_test.py +++ b/test/cli/other_test.py @@ -4575,6 +4575,47 @@ def run_and_assert_cppcheck(stdout_exp): # - internalError +def test_analyzerinfo_include_order(tmp_path): # #15132 + with open(tmp_path / 'z.h', 'wt') as f: + f.write('int z;\n') + with open(tmp_path / 'a.h', 'wt') as f: + f.write('int a;\n') + test_file = tmp_path / 'test.c' + with open(test_file, 'wt') as f: + f.write( +"""#include "z.h" +#include "a.h" +int f(void) { return a + z; } +""") + + build_dir = tmp_path / 'b1' + os.makedirs(build_dir) + + args = [ + '-q', + '--debug-analyzerinfo', + '--cppcheck-build-dir={}'.format(build_dir), + str(test_file) + ] + + test_file_s = str(test_file).replace('\\', '/') + test_a1_file_s = str(build_dir / 'test.a1').replace('\\', '/') + + exitcode, stdout, stderr = cppcheck(args) + assert exitcode == 0, stdout + assert stdout.splitlines() == [ + "no cached result '{}' for '{}' found".format(test_a1_file_s, test_file_s) + ] + assert stderr.splitlines() == [] + + exitcode, stdout, stderr = cppcheck(args) + assert exitcode == 0, stdout + assert stdout.splitlines() == [ + "skipping analysis - loaded 0 cached finding(s) from '{}' for '{}'".format(test_a1_file_s, test_file_s) + ] + assert stderr.splitlines() == [] + + def test_ctu_function_call_path_slash(tmp_path): # #14591 test_file = tmp_path / 'test.cpp' with open(test_file, "w") as f: diff --git a/test/testpreprocessor.cpp b/test/testpreprocessor.cpp index 9aad97b2c99..faa1d9ce01b 100644 --- a/test/testpreprocessor.cpp +++ b/test/testpreprocessor.cpp @@ -382,6 +382,7 @@ class TestPreprocessor : public TestFixture { TEST_CASE(limitsDefines); TEST_CASE(hashCalculation); + TEST_CASE(hashCalculationIncludeOrder); TEST_CASE(standard); @@ -3182,6 +3183,25 @@ class TestPreprocessor : public TestFixture { ASSERT(getHash(code2) != getHash(code3)); } + void hashCalculationIncludeOrder() { + // #15132 - the load order of the includes must not change the hash + const ScopedFile headerA("hash_a.h", "int a;\n"); + const ScopedFile headerZ("hash_z.h", "int z;\n"); + const char code[] = "#include \"hash_z.h\"\n" + "#include \"hash_a.h\"\n"; + + const auto getHashLoadOrder = [&](const std::vector &loadOrder) { + std::vector files; + simplecpp::TokenList tokens(code, files, "test.c"); + Preprocessor preprocessor(tokens, settingsDefault, *this, Standards::Language::C); + for (const std::string &header : loadOrder) + ASSERT(preprocessor.loadFile(files, header)); + return preprocessor.calculateHash(""); + }; + + ASSERT_EQUALS(getHashLoadOrder({"hash_z.h", "hash_a.h"}), getHashLoadOrder({"hash_a.h", "hash_z.h"})); + } + void standard() { const char code[] = "int a;\n";