Skip to content

Commit 5e437ff

Browse files
committed
Fix #15013 (warning hash: calculate hash for CTU warnings)
1 parent f9ca1b4 commit 5e437ff

3 files changed

Lines changed: 107 additions & 0 deletions

File tree

lib/errorlogger.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ ErrorMessage::ErrorMessage()
6565
: severity(Severity::none), cwe(0U), certainty(Certainty::normal)
6666
{}
6767

68+
static bool needsFallbackHash(const std::string &id)
69+
{
70+
return startsWith(id, "ctu") || id == "unusedFunction" || id == "staticFunction";
71+
}
72+
6873
// TODO: id and msg are swapped compared to other calls
6974
ErrorMessage::ErrorMessage(std::list<FileLocation> callStack, std::string file1, Severity severity, const std::string &msg, std::string id, Certainty certainty) :
7075
callStack(std::move(callStack)), // locations for this error message
@@ -76,6 +81,9 @@ ErrorMessage::ErrorMessage(std::list<FileLocation> callStack, std::string file1,
7681
{
7782
// set the summary and verbose messages
7883
setmsg(msg);
84+
85+
if (hash == 0 && needsFallbackHash(this->id))
86+
calculateWarningHashFromLocations();
7987
}
8088

8189

@@ -90,6 +98,9 @@ ErrorMessage::ErrorMessage(std::list<FileLocation> callStack, std::string file1,
9098
{
9199
// set the summary and verbose messages
92100
setmsg(msg);
101+
102+
if (hash == 0 && needsFallbackHash(this->id))
103+
calculateWarningHashFromLocations();
93104
}
94105

95106
ErrorMessage::ErrorMessage(const std::list<const Token*>& callstack, const TokenList* list, Severity severity, std::string id, const std::string& msg, Certainty certainty)
@@ -305,6 +316,23 @@ void ErrorMessage::calculateWarningHash(const std::list<const Token*>& callstack
305316
});
306317
}
307318

319+
void ErrorMessage::calculateWarningHashFromLocations()
320+
{
321+
// No token information is available for this warning (e.g. whole-program/CTU
322+
// checks, unusedFunction, staticFunction) so calculateWarningHash() can't be
323+
// used. Instead hash the id, message and all filenames/notes in the callstack.
324+
std::string hashString = id + '\n' + mShortMessage;
325+
for (const FileLocation &loc : callStack) {
326+
hashString += '\n' + loc.getfile(false) + '\n' + loc.getinfo();
327+
}
328+
329+
// hash algorithm: sdbm
330+
// any hash algorithm can be used but it has to be the same hash on different platforms and compilers
331+
hash = std::accumulate(hashString.cbegin(), hashString.cend(), std::size_t{0}, [](std::size_t h, unsigned char c) {
332+
return static_cast<std::size_t>(c) + (h << 6) + (h << 16) - h;
333+
});
334+
}
335+
308336
static void serializeString(std::string &oss, const std::string & str)
309337
{
310338
oss += std::to_string(str.length());

lib/errorlogger.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,13 @@ class CPPCHECKLIB ErrorMessage {
211211

212212
void calculateWarningHash(const std::list<const Token*>& callstack);
213213

214+
/**
215+
* Fallback hash calculation for warnings that have no token information
216+
* (e.g. whole-program/CTU checks, unusedFunction, staticFunction). Hashes
217+
* the id, message and all filenames/notes in the callstack instead.
218+
*/
219+
void calculateWarningHashFromLocations();
220+
214221
/** Short message */
215222
std::string mShortMessage;
216223

test/testerrorlogger.cpp

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ class TestErrorLogger : public TestFixture {
4949
TEST_CASE(FileLocationSetFile2);
5050
TEST_CASE(ErrorMessageConstruct);
5151
TEST_CASE(ErrorMessageConstructLocations);
52+
TEST_CASE(ErrorMessageHashFallback);
5253
TEST_CASE(ErrorMessageVerbose);
5354
TEST_CASE(ErrorMessageVerboseLocations);
5455
TEST_CASE(ErrorMessageVerboseSymbol);
@@ -267,6 +268,77 @@ class TestErrorLogger : public TestFixture {
267268
ASSERT_EQUALS("[foo.cpp:5] -> [bar.cpp:8]: (error) Programming error.", msg.toString(true, templateFormat, ""));
268269
}
269270

271+
// unusedFunction/staticFunction/ctu* warnings carry no token information, so
272+
// ErrorMessage::calculateWarningHash() (which needs tokens) can't compute a
273+
// hash for them. calculateWarningHashFromLocations() is the fallback used
274+
// instead - it hashes the id, message and all location filenames/notes.
275+
void ErrorMessageHashFallback() const {
276+
// ids that don't need a fallback hash still get none
277+
{
278+
std::list<ErrorMessage::FileLocation> locs(1, fooCpp5);
279+
ErrorMessage msg(std::move(locs), "", Severity::style, "Some warning.", "someOtherId", Certainty::normal);
280+
ASSERT_EQUALS(0, msg.hash);
281+
}
282+
283+
// unusedFunction, staticFunction and any ctu* id get a non-zero fallback hash
284+
for (const std::string& id : { std::string("unusedFunction"), std::string("staticFunction"), std::string("ctuOneDefinitionRuleViolation") }) {
285+
std::list<ErrorMessage::FileLocation> locs(1, fooCpp5);
286+
ErrorMessage msg(std::move(locs), "", Severity::style, "Some warning.", id, Certainty::normal);
287+
ASSERT(msg.hash != 0);
288+
}
289+
290+
// same id/message/locations => same hash
291+
{
292+
std::list<ErrorMessage::FileLocation> locs1(1, fooCpp5);
293+
ErrorMessage msg1(std::move(locs1), "", Severity::style, "Some warning.", "unusedFunction", Certainty::normal);
294+
295+
std::list<ErrorMessage::FileLocation> locs2(1, fooCpp5);
296+
ErrorMessage msg2(std::move(locs2), "", Severity::style, "Some warning.", "unusedFunction", Certainty::normal);
297+
298+
ASSERT_EQUALS(msg1.hash, msg2.hash);
299+
}
300+
301+
// different message => different hash
302+
{
303+
std::list<ErrorMessage::FileLocation> locs1(1, fooCpp5);
304+
ErrorMessage msg1(std::move(locs1), "", Severity::style, "Some warning.", "unusedFunction", Certainty::normal);
305+
306+
std::list<ErrorMessage::FileLocation> locs2(1, fooCpp5);
307+
ErrorMessage msg2(std::move(locs2), "", Severity::style, "Some other warning.", "unusedFunction", Certainty::normal);
308+
309+
ASSERT(msg1.hash != msg2.hash);
310+
}
311+
312+
// different location filename => different hash
313+
{
314+
std::list<ErrorMessage::FileLocation> locs1(1, fooCpp5);
315+
ErrorMessage msg1(std::move(locs1), "", Severity::style, "Some warning.", "unusedFunction", Certainty::normal);
316+
317+
std::list<ErrorMessage::FileLocation> locs2(1, barCpp8);
318+
ErrorMessage msg2(std::move(locs2), "", Severity::style, "Some warning.", "unusedFunction", Certainty::normal);
319+
320+
ASSERT(msg1.hash != msg2.hash);
321+
}
322+
323+
// different location note (info) => different hash
324+
{
325+
std::list<ErrorMessage::FileLocation> locs1(1, barCpp8);
326+
ErrorMessage msg1(std::move(locs1), "", Severity::error, "Some warning.", "ctuOneDefinitionRuleViolation", Certainty::normal);
327+
328+
std::list<ErrorMessage::FileLocation> locs2(1, barCpp8_i);
329+
ErrorMessage msg2(std::move(locs2), "", Severity::error, "Some warning.", "ctuOneDefinitionRuleViolation", Certainty::normal);
330+
331+
ASSERT(msg1.hash != msg2.hash);
332+
}
333+
334+
// hash shows up in the XML output
335+
{
336+
std::list<ErrorMessage::FileLocation> locs(1, fooCpp5);
337+
ErrorMessage msg(std::move(locs), "", Severity::style, "Some warning.", "unusedFunction", Certainty::normal);
338+
ASSERT(msg.toXML().find(" hash=\"") != std::string::npos);
339+
}
340+
}
341+
270342
void ErrorMessageVerbose() const {
271343
std::list<ErrorMessage::FileLocation> locs(1, fooCpp5);
272344
ErrorMessage msg(std::move(locs), "", Severity::error, "Programming error.\nVerbose error", "errorId", Certainty::normal);

0 commit comments

Comments
 (0)