Skip to content

Commit 71a4af2

Browse files
authored
fixed #13663/#14232 - reworked unmatchedSuppression exclusion logic / do not report misra-* or premium-* suppressions when they are not enabled (#7925)
1 parent a4a4a24 commit 71a4af2

File tree

12 files changed

+267
-145
lines changed

12 files changed

+267
-145
lines changed

cli/cmdlineparser.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1609,11 +1609,22 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a
16091609
if (mSettings.jobs > 1 && mSettings.buildDir.empty()) {
16101610
// TODO: bail out instead?
16111611
if (mSettings.checks.isEnabled(Checks::unusedFunction))
1612+
{
16121613
mLogger.printMessage("unusedFunction check requires --cppcheck-build-dir to be active with -j.");
1614+
mSettings.checks.disable(Checks::unusedFunction);
1615+
// TODO: is there some later logic to remove?
1616+
}
16131617
// TODO: enable
16141618
//mLogger.printMessage("whole program analysis requires --cppcheck-build-dir to be active with -j.");
16151619
}
16161620

1621+
if (!mSettings.checks.isEnabled(Checks::unusedFunction))
1622+
mSettings.unmatchedSuppressionFilters.emplace_back("unusedFunction");
1623+
if (!mSettings.addons.count("misra"))
1624+
mSettings.unmatchedSuppressionFilters.emplace_back("misra-*");
1625+
if (!mSettings.premium)
1626+
mSettings.unmatchedSuppressionFilters.emplace_back("premium-*");
1627+
16171628
if (inputAsFilter) {
16181629
mSettings.fileFilters.insert(mSettings.fileFilters.end(), mPathNames.cbegin(), mPathNames.cend());
16191630
mPathNames.clear();

cli/cppcheckexecutor.cpp

Lines changed: 58 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -293,35 +293,73 @@ int CppCheckExecutor::check_wrapper(const Settings& settings, Suppressions& supp
293293
return check_internal(settings, supprs);
294294
}
295295

296-
bool CppCheckExecutor::reportSuppressions(const Settings &settings, const SuppressionList& suppressions, bool unusedFunctionCheckEnabled, const std::list<FileWithDetails> &files, const std::list<FileSettings>& fileSettings, ErrorLogger& errorLogger) {
297-
const auto& suppr = suppressions.getSuppressions();
298-
if (std::any_of(suppr.begin(), suppr.end(), [](const SuppressionList::Suppression& s) {
299-
return s.errorId == "unmatchedSuppression" && s.fileName.empty() && s.lineNumber == SuppressionList::Suppression::NO_LINE;
296+
/**
297+
* Report unmatched suppressions
298+
* @param unmatched list of unmatched suppressions (from Settings::Suppressions::getUnmatched(Local|Global)Suppressions)
299+
* @return true is returned if errors are reported
300+
*/
301+
static bool reportUnmatchedSuppressions(const std::list<SuppressionList::Suppression> &unmatched, ErrorLogger &errorLogger, const std::vector<std::string>& filters)
302+
{
303+
bool err = false;
304+
// Report unmatched suppressions
305+
for (const SuppressionList::Suppression &s : unmatched) {
306+
// check if this unmatched suppression is suppressed
307+
bool suppressed = false;
308+
for (const SuppressionList::Suppression &s2 : unmatched) {
309+
if (s2.errorId == "unmatchedSuppression") {
310+
if ((s2.fileName.empty() || s2.fileName == "*" || s2.fileName == s.fileName) &&
311+
(s2.lineNumber == SuppressionList::Suppression::NO_LINE || s2.lineNumber == s.lineNumber)) {
312+
suppressed = true;
313+
break;
314+
}
315+
}
316+
}
317+
318+
if (suppressed)
319+
continue;
320+
321+
const bool skip = std::any_of(filters.cbegin(), filters.cend(), [&s](const std::string& filter) {
322+
return matchglob(filter, s.errorId);
323+
});
324+
if (skip)
325+
continue;
326+
327+
std::list<::ErrorMessage::FileLocation> callStack;
328+
if (!s.fileName.empty()) {
329+
callStack.emplace_back(s.fileName, s.lineNumber, 0);
330+
}
331+
errorLogger.reportErr(::ErrorMessage(std::move(callStack), "", Severity::information, "Unmatched suppression: " + s.errorId, "unmatchedSuppression", Certainty::normal));
332+
err = true;
333+
}
334+
return err;
335+
}
336+
337+
bool CppCheckExecutor::reportUnmatchedSuppressions(const Settings &settings, const SuppressionList& suppressions, const std::list<FileWithDetails> &files, const std::list<FileSettings>& fileSettings, ErrorLogger& errorLogger) {
338+
// the two inputs may only be used exclusively
339+
assert(!(!files.empty() && !fileSettings.empty()));
340+
341+
// bail out if there is a suppression of unmatchedSuppression which matches any file
342+
const auto suppr = suppressions.getSuppressions();
343+
if (std::any_of(suppr.cbegin(), suppr.cend(), [](const SuppressionList::Suppression& s) {
344+
return s.errorId == "unmatchedSuppression" && (s.fileName.empty() || s.fileName == "*") && s.lineNumber == SuppressionList::Suppression::NO_LINE;
300345
}))
301346
return false;
302347

303348
bool err = false;
304-
if (settings.useSingleJob()) {
305-
// the two inputs may only be used exclusively
306-
assert(!(!files.empty() && !fileSettings.empty()));
307349

308-
for (auto i = files.cbegin(); i != files.cend(); ++i) {
309-
err |= SuppressionList::reportUnmatchedSuppressions(
310-
suppressions.getUnmatchedLocalSuppressions(*i, unusedFunctionCheckEnabled), errorLogger);
311-
}
350+
for (auto i = files.cbegin(); i != files.cend(); ++i) {
351+
err |= ::reportUnmatchedSuppressions(suppressions.getUnmatchedLocalSuppressions(*i), errorLogger, settings.unmatchedSuppressionFilters);
352+
}
312353

313-
for (auto i = fileSettings.cbegin(); i != fileSettings.cend(); ++i) {
314-
err |= SuppressionList::reportUnmatchedSuppressions(
315-
suppressions.getUnmatchedLocalSuppressions(i->file, unusedFunctionCheckEnabled), errorLogger);
316-
}
354+
for (auto i = fileSettings.cbegin(); i != fileSettings.cend(); ++i) {
355+
err |= ::reportUnmatchedSuppressions(suppressions.getUnmatchedLocalSuppressions(i->file), errorLogger, settings.unmatchedSuppressionFilters);
317356
}
357+
318358
if (settings.inlineSuppressions) {
319-
// report unmatched unusedFunction suppressions
320-
err |= SuppressionList::reportUnmatchedSuppressions(
321-
suppressions.getUnmatchedInlineSuppressions(unusedFunctionCheckEnabled), errorLogger);
359+
err |= ::reportUnmatchedSuppressions(suppressions.getUnmatchedInlineSuppressions(), errorLogger, settings.unmatchedSuppressionFilters);
322360
}
323361

324-
err |= SuppressionList::reportUnmatchedSuppressions(suppressions.getUnmatchedGlobalSuppressions(unusedFunctionCheckEnabled), errorLogger);
362+
err |= ::reportUnmatchedSuppressions(suppressions.getUnmatchedGlobalSuppressions(), errorLogger, settings.unmatchedSuppressionFilters);
325363
return err;
326364
}
327365

@@ -376,7 +414,7 @@ int CppCheckExecutor::check_internal(const Settings& settings, Suppressions& sup
376414
returnValue |= cppcheck.analyseWholeProgram(settings.buildDir, mFiles, mFileSettings, stdLogger.getCtuInfo());
377415

378416
if (settings.severity.isEnabled(Severity::information) || settings.checkConfiguration) {
379-
const bool err = reportSuppressions(settings, supprs.nomsg, settings.checks.isEnabled(Checks::unusedFunction), mFiles, mFileSettings, stdLogger);
417+
const bool err = reportUnmatchedSuppressions(settings, supprs.nomsg, mFiles, mFileSettings, stdLogger);
380418
if (err && returnValue == 0)
381419
returnValue = settings.exitCode;
382420
}

cli/cppcheckexecutor.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ class CppCheckExecutor {
7070

7171
protected:
7272

73-
static bool reportSuppressions(const Settings &settings, const SuppressionList& suppressions, bool unusedFunctionCheckEnabled, const std::list<FileWithDetails> &files, const std::list<FileSettings>& fileSettings, ErrorLogger& errorLogger);
73+
static bool reportUnmatchedSuppressions(const Settings &settings, const SuppressionList& suppressions, const std::list<FileWithDetails> &files, const std::list<FileSettings>& fileSettings, ErrorLogger& errorLogger);
7474

7575
/**
7676
* Wrapper around check_internal

lib/cppcheck.cpp

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1299,16 +1299,6 @@ unsigned int CppCheck::checkInternal(const FileWithDetails& file, const std::str
12991299
mErrorLogger.reportErr(errmsg);
13001300
}
13011301

1302-
// TODO: this is done too early causing the whole program analysis suppressions to be reported as unmatched
1303-
if (mSettings.severity.isEnabled(Severity::information) || mSettings.checkConfiguration) {
1304-
// In jointSuppressionReport mode, unmatched suppressions are
1305-
// collected after all files are processed
1306-
if (!mSettings.useSingleJob()) {
1307-
// TODO: check result?
1308-
SuppressionList::reportUnmatchedSuppressions(mSuppressions.nomsg.getUnmatchedLocalSuppressions(file, static_cast<bool>(mUnusedFunctionsCheck)), mErrorLogger);
1309-
}
1310-
}
1311-
13121302
if (analyzerInformation) {
13131303
mLogger->setAnalyzerInfo(nullptr);
13141304
analyzerInformation.reset();

lib/settings.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,9 @@ class CPPCHECKLIB WARN_UNUSED Settings {
435435
/** @brief The maximum time in seconds for the typedef simplification */
436436
std::size_t typedefMaxTime{};
437437

438+
/** @brief Error IDs which should not be reported as unmatchedSuppression */
439+
std::vector<std::string> unmatchedSuppressionFilters;
440+
438441
/** @brief defines given by the user */
439442
std::string userDefines;
440443

lib/suppressions.cpp

Lines changed: 7 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737

3838
#include "xml.h"
3939

40-
static const char ID_UNUSEDFUNCTION[] = "unusedFunction";
4140
static const char ID_CHECKERSREPORT[] = "checkersReport";
4241

4342
SuppressionList::ErrorMessage SuppressionList::ErrorMessage::fromErrorMessage(const ::ErrorMessage &msg, const std::set<std::string> &macroNames)
@@ -312,13 +311,10 @@ bool SuppressionList::updateSuppressionState(const SuppressionList::Suppression&
312311
auto foundSuppression = std::find_if(mSuppressions.begin(), mSuppressions.end(),
313312
std::bind(&Suppression::isSameParameters, &suppression, std::placeholders::_1));
314313
if (foundSuppression != mSuppressions.end()) {
315-
// Update state of existing global suppression
316-
if (!suppression.isLocal()) {
317-
if (suppression.checked)
318-
foundSuppression->checked = true;
319-
if (suppression.matched)
320-
foundSuppression->matched = true;
321-
}
314+
if (suppression.checked)
315+
foundSuppression->checked = true;
316+
if (suppression.matched)
317+
foundSuppression->matched = true;
322318
return true;
323319
}
324320

@@ -555,7 +551,7 @@ void SuppressionList::dump(std::ostream & out, const std::string& filePath) cons
555551
out << " </suppressions>" << std::endl;
556552
}
557553

558-
std::list<SuppressionList::Suppression> SuppressionList::getUnmatchedLocalSuppressions(const FileWithDetails &file, const bool includeUnusedFunction) const
554+
std::list<SuppressionList::Suppression> SuppressionList::getUnmatchedLocalSuppressions(const FileWithDetails &file) const
559555
{
560556
std::lock_guard<std::mutex> lg(mSuppressionsSync);
561557

@@ -573,16 +569,14 @@ std::list<SuppressionList::Suppression> SuppressionList::getUnmatchedLocalSuppre
573569
continue;
574570
if (s.errorId == ID_CHECKERSREPORT)
575571
continue;
576-
if (!includeUnusedFunction && s.errorId == ID_UNUSEDFUNCTION)
577-
continue;
578572
if (!s.isLocal() || s.fileName != file.spath())
579573
continue;
580574
result.push_back(s);
581575
}
582576
return result;
583577
}
584578

585-
std::list<SuppressionList::Suppression> SuppressionList::getUnmatchedGlobalSuppressions(const bool includeUnusedFunction) const
579+
std::list<SuppressionList::Suppression> SuppressionList::getUnmatchedGlobalSuppressions() const
586580
{
587581
std::lock_guard<std::mutex> lg(mSuppressionsSync);
588582

@@ -596,8 +590,6 @@ std::list<SuppressionList::Suppression> SuppressionList::getUnmatchedGlobalSuppr
596590
continue;
597591
if (s.hash > 0)
598592
continue;
599-
if (!includeUnusedFunction && s.errorId == ID_UNUSEDFUNCTION)
600-
continue;
601593
if (s.errorId == ID_CHECKERSREPORT)
602594
continue;
603595
if (s.isLocal())
@@ -607,7 +599,7 @@ std::list<SuppressionList::Suppression> SuppressionList::getUnmatchedGlobalSuppr
607599
return result;
608600
}
609601

610-
std::list<SuppressionList::Suppression> SuppressionList::getUnmatchedInlineSuppressions(const bool includeUnusedFunction) const
602+
std::list<SuppressionList::Suppression> SuppressionList::getUnmatchedInlineSuppressions() const
611603
{
612604
std::list<SuppressionList::Suppression> result;
613605
for (const SuppressionList::Suppression &s : SuppressionList::mSuppressions) {
@@ -620,8 +612,6 @@ std::list<SuppressionList::Suppression> SuppressionList::getUnmatchedInlineSuppr
620612
continue;
621613
if (s.hash > 0)
622614
continue;
623-
if (!includeUnusedFunction && s.errorId == ID_UNUSEDFUNCTION)
624-
continue;
625615
result.push_back(s);
626616
}
627617
return result;
@@ -660,39 +650,6 @@ void SuppressionList::markUnmatchedInlineSuppressionsAsChecked(const Tokenizer &
660650
}
661651
}
662652

663-
bool SuppressionList::reportUnmatchedSuppressions(const std::list<SuppressionList::Suppression> &unmatched, ErrorLogger &errorLogger)
664-
{
665-
bool err = false;
666-
// Report unmatched suppressions
667-
for (const SuppressionList::Suppression &s : unmatched) {
668-
// don't report "unmatchedSuppression" as unmatched
669-
if (s.errorId == "unmatchedSuppression")
670-
continue;
671-
672-
// check if this unmatched suppression is suppressed
673-
bool suppressed = false;
674-
for (const SuppressionList::Suppression &s2 : unmatched) {
675-
if (s2.errorId == "unmatchedSuppression") {
676-
if ((s2.fileName.empty() || s2.fileName == "*" || s2.fileName == s.fileName) &&
677-
(s2.lineNumber == SuppressionList::Suppression::NO_LINE || s2.lineNumber == s.lineNumber)) {
678-
suppressed = true;
679-
break;
680-
}
681-
}
682-
}
683-
684-
if (suppressed)
685-
continue;
686-
687-
std::list<::ErrorMessage::FileLocation> callStack;
688-
if (!s.fileName.empty())
689-
callStack.emplace_back(s.fileName, s.lineNumber, 0);
690-
errorLogger.reportErr(::ErrorMessage(std::move(callStack), "", Severity::information, "Unmatched suppression: " + s.errorId, "unmatchedSuppression", Certainty::normal));
691-
err = true;
692-
}
693-
return err;
694-
}
695-
696653
std::string SuppressionList::Suppression::toString() const
697654
{
698655
std::string s;

lib/suppressions.h

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434

3535
class Tokenizer;
3636
class ErrorMessage;
37-
class ErrorLogger;
3837
enum class Certainty : std::uint8_t;
3938
class FileWithDetails;
4039

@@ -258,19 +257,19 @@ class CPPCHECKLIB SuppressionList {
258257
* @brief Returns list of unmatched local (per-file) suppressions.
259258
* @return list of unmatched suppressions
260259
*/
261-
std::list<Suppression> getUnmatchedLocalSuppressions(const FileWithDetails &file, bool includeUnusedFunction) const;
260+
std::list<Suppression> getUnmatchedLocalSuppressions(const FileWithDetails &file) const;
262261

263262
/**
264263
* @brief Returns list of unmatched global (glob pattern) suppressions.
265264
* @return list of unmatched suppressions
266265
*/
267-
std::list<Suppression> getUnmatchedGlobalSuppressions(bool includeUnusedFunction) const;
266+
std::list<Suppression> getUnmatchedGlobalSuppressions() const;
268267

269268
/**
270269
* @brief Returns list of unmatched inline suppressions.
271270
* @return list of unmatched suppressions
272271
*/
273-
std::list<Suppression> getUnmatchedInlineSuppressions(bool includeUnusedFunction) const;
272+
std::list<Suppression> getUnmatchedInlineSuppressions() const;
274273

275274
/**
276275
* @brief Returns list of all suppressions.
@@ -283,13 +282,6 @@ class CPPCHECKLIB SuppressionList {
283282
*/
284283
void markUnmatchedInlineSuppressionsAsChecked(const Tokenizer &tokenizer);
285284

286-
/**
287-
* Report unmatched suppressions
288-
* @param unmatched list of unmatched suppressions (from Settings::Suppressions::getUnmatched(Local|Global)Suppressions)
289-
* @return true is returned if errors are reported
290-
*/
291-
static bool reportUnmatchedSuppressions(const std::list<SuppressionList::Suppression> &unmatched, ErrorLogger &errorLogger);
292-
293285
private:
294286
mutable std::mutex mSuppressionsSync;
295287
/** @brief List of error which the user doesn't want to see. */

test/cli/inline-suppress_test.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,3 +501,60 @@ def test_unmatched_cfg():
501501
]
502502
assert stdout == ''
503503
assert ret == 0, stdout
504+
505+
506+
# do not report unmatched unusedFunction inline suppressions when unusedFunction check is disabled
507+
# unusedFunction is disabled when -j2 is specified without a builddir
508+
def test_unused_function_disabled_unmatched_j():
509+
args = [
510+
'-q',
511+
'--template=simple',
512+
'--enable=warning,information',
513+
'--inline-suppr',
514+
'-j2',
515+
'--no-cppcheck-build-dir',
516+
'proj-inline-suppress/unusedFunctionUnmatched.cpp'
517+
]
518+
519+
ret, stdout, stderr = cppcheck(args, cwd=__script_dir)
520+
assert stderr.splitlines() == [
521+
'{}unusedFunctionUnmatched.cpp:5:0: information: Unmatched suppression: uninitvar [unmatchedSuppression]'.format(__proj_inline_suppres_path)
522+
]
523+
assert stdout == ''
524+
assert ret == 0, stdout
525+
526+
527+
# do not report unmatched misra-* inline suppressions when misra is not provided
528+
def test_misra_disabled_unmatched(): #14232
529+
args = [
530+
'-q',
531+
'--template=simple',
532+
'--enable=warning,information',
533+
'--inline-suppr',
534+
'proj-inline-suppress/misraUnmatched.c'
535+
]
536+
537+
ret, stdout, stderr = cppcheck(args, cwd=__script_dir)
538+
assert stderr.splitlines() == [
539+
'{}misraUnmatched.c:5:0: information: Unmatched suppression: uninitvar [unmatchedSuppression]'.format(__proj_inline_suppres_path)
540+
]
541+
assert stdout == ''
542+
assert ret == 0, stdout
543+
544+
545+
# do not report unmatched premium-* inline suppressions when application is not premium
546+
def test_premium_disabled_unmatched(): #13663
547+
args = [
548+
'-q',
549+
'--template=simple',
550+
'--enable=warning,information',
551+
'--inline-suppr',
552+
'proj-inline-suppress/premiumUnmatched.cpp'
553+
]
554+
555+
ret, stdout, stderr = cppcheck(args, cwd=__script_dir)
556+
assert stderr.splitlines() == [
557+
'{}premiumUnmatched.cpp:5:0: information: Unmatched suppression: uninitvar [unmatchedSuppression]'.format(__proj_inline_suppres_path)
558+
]
559+
assert stdout == ''
560+
assert ret == 0, stdout

0 commit comments

Comments
 (0)