Skip to content

Commit 7828b1b

Browse files
authored
Fix #15132 build dir: cached results are discarded on the second run when includes are not in order (#8957)
1 parent 78a38ed commit 7828b1b

3 files changed

Lines changed: 78 additions & 10 deletions

File tree

‎lib/preprocessor.cpp‎

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1114,22 +1114,29 @@ void Preprocessor::dump(std::ostream &out) const
11141114
std::size_t Preprocessor::calculateHash(const std::string &toolinfo) const
11151115
{
11161116
std::string hashData = toolinfo;
1117-
for (const simplecpp::Token *tok = mTokens.cfront(); tok; tok = tok->next) {
1118-
if (!tok->comment) {
1119-
hashData += tok->str();
1120-
hashData += static_cast<char>(tok->location.line);
1121-
hashData += static_cast<char>(tok->location.col);
1122-
}
1123-
}
1124-
for (const auto &filedata : mFileCache) {
1125-
for (const simplecpp::Token *tok = filedata->tokens.cfront(); tok; tok = tok->next) {
1117+
const auto addTokens = [&hashData](const simplecpp::TokenList &tokens) {
1118+
for (const simplecpp::Token *tok = tokens.cfront(); tok; tok = tok->next) {
11261119
if (!tok->comment) {
11271120
hashData += tok->str();
11281121
hashData += static_cast<char>(tok->location.line);
11291122
hashData += static_cast<char>(tok->location.col);
11301123
}
11311124
}
1132-
}
1125+
};
1126+
addTokens(mTokens);
1127+
1128+
// Hash the included files in a fixed order, the order they are loaded in differs between runs
1129+
std::vector<const simplecpp::FileData *> fileData;
1130+
fileData.reserve(mFileCache.size());
1131+
std::transform(mFileCache.cbegin(), mFileCache.cend(), std::back_inserter(fileData), [](const std::unique_ptr<simplecpp::FileData> &data) {
1132+
return data.get();
1133+
});
1134+
std::sort(fileData.begin(), fileData.end(), [](const simplecpp::FileData *a, const simplecpp::FileData *b) {
1135+
return a->filename < b->filename;
1136+
});
1137+
for (const simplecpp::FileData *data : fileData)
1138+
addTokens(data->tokens);
1139+
11331140
return (std::hash<std::string>{})(hashData);
11341141
}
11351142

‎test/cli/other_test.py‎

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4575,6 +4575,47 @@ def run_and_assert_cppcheck(stdout_exp):
45754575
# - internalError
45764576

45774577

4578+
def test_analyzerinfo_include_order(tmp_path): # #15132
4579+
with open(tmp_path / 'z.h', 'wt') as f:
4580+
f.write('int z;\n')
4581+
with open(tmp_path / 'a.h', 'wt') as f:
4582+
f.write('int a;\n')
4583+
test_file = tmp_path / 'test.c'
4584+
with open(test_file, 'wt') as f:
4585+
f.write(
4586+
"""#include "z.h"
4587+
#include "a.h"
4588+
int f(void) { return a + z; }
4589+
""")
4590+
4591+
build_dir = tmp_path / 'b1'
4592+
os.makedirs(build_dir)
4593+
4594+
args = [
4595+
'-q',
4596+
'--debug-analyzerinfo',
4597+
'--cppcheck-build-dir={}'.format(build_dir),
4598+
str(test_file)
4599+
]
4600+
4601+
test_file_s = str(test_file).replace('\\', '/')
4602+
test_a1_file_s = str(build_dir / 'test.a1').replace('\\', '/')
4603+
4604+
exitcode, stdout, stderr = cppcheck(args)
4605+
assert exitcode == 0, stdout
4606+
assert stdout.splitlines() == [
4607+
"no cached result '{}' for '{}' found".format(test_a1_file_s, test_file_s)
4608+
]
4609+
assert stderr.splitlines() == []
4610+
4611+
exitcode, stdout, stderr = cppcheck(args)
4612+
assert exitcode == 0, stdout
4613+
assert stdout.splitlines() == [
4614+
"skipping analysis - loaded 0 cached finding(s) from '{}' for '{}'".format(test_a1_file_s, test_file_s)
4615+
]
4616+
assert stderr.splitlines() == []
4617+
4618+
45784619
def test_ctu_function_call_path_slash(tmp_path): # #14591
45794620
test_file = tmp_path / 'test.cpp'
45804621
with open(test_file, "w") as f:

‎test/testpreprocessor.cpp‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,7 @@ class TestPreprocessor : public TestFixture {
382382
TEST_CASE(limitsDefines);
383383

384384
TEST_CASE(hashCalculation);
385+
TEST_CASE(hashCalculationIncludeOrder);
385386

386387
TEST_CASE(standard);
387388

@@ -3182,6 +3183,25 @@ class TestPreprocessor : public TestFixture {
31823183
ASSERT(getHash(code2) != getHash(code3));
31833184
}
31843185

3186+
void hashCalculationIncludeOrder() {
3187+
// #15132 - the load order of the includes must not change the hash
3188+
const ScopedFile headerA("hash_a.h", "int a;\n");
3189+
const ScopedFile headerZ("hash_z.h", "int z;\n");
3190+
const char code[] = "#include \"hash_z.h\"\n"
3191+
"#include \"hash_a.h\"\n";
3192+
3193+
const auto getHashLoadOrder = [&](const std::vector<std::string> &loadOrder) {
3194+
std::vector<std::string> files;
3195+
simplecpp::TokenList tokens(code, files, "test.c");
3196+
Preprocessor preprocessor(tokens, settingsDefault, *this, Standards::Language::C);
3197+
for (const std::string &header : loadOrder)
3198+
ASSERT(preprocessor.loadFile(files, header));
3199+
return preprocessor.calculateHash("");
3200+
};
3201+
3202+
ASSERT_EQUALS(getHashLoadOrder({"hash_z.h", "hash_a.h"}), getHashLoadOrder({"hash_a.h", "hash_z.h"}));
3203+
}
3204+
31853205
void standard() {
31863206

31873207
const char code[] = "int a;\n";

0 commit comments

Comments
 (0)