Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refs #12171 / fixed #12347 - improved handling of (unmatched) local and inline suppressions #5647

Merged
merged 18 commits into from
Feb 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
a3b17eb
split off handling of inline suppressions
firewave Jan 5, 2024
16040b2
adjustments for unmatched `unusedFunction` inline suppressions
firewave Mar 29, 2024
411ebe8
updated some tests related to suppressions
firewave Jan 4, 2025
7f78cfa
ProcessExecutor: transfer inline suppressions back to the main process
firewave Jan 5, 2025
75df6f8
ProcessExecutor: transfer checked/matched state of inline suppressions
firewave Jan 13, 2025
8e0b94a
do not report unmatched inline suppressions until the whole program a…
firewave Jan 13, 2025
0e081ed
transfer back all inline suppressions from projects
firewave Jan 13, 2025
f864c82
test/cli/whole-program_test.py: removed unnecessary overrides
firewave Jan 13, 2025
9501129
ThreadExecutor: transfer back all inline suppressions
firewave Jan 13, 2025
00a0130
ProcessExecutor: make transferred back inline suppressions actually i…
firewave Jan 13, 2025
8e5b975
cleaned up SuppressionList changes
firewave Jan 13, 2025
526c81a
updated inline-suppress_test.py
firewave Jan 13, 2025
82c4893
preprocessor.cpp: set `Suppression::isInline` earlier
firewave Jan 14, 2025
ea31818
test/cli/other_test.py: xfail'd some test regressions
firewave Jan 27, 2025
b8bfd38
test/cli/whole-program_test.py: xfail'd some test regressions
firewave Jan 27, 2025
aede3e6
test/cli/inline-suppress_test.py: skip tests of (currently) undefined…
firewave Jan 27, 2025
2024842
update state of inline suppressions if they already exist
firewave Jan 29, 2025
8fd396f
cli: fixed compilation
firewave Feb 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -682,7 +682,7 @@ cli/singleexecutor.o: cli/singleexecutor.cpp cli/executor.h cli/singleexecutor.h
cli/stacktrace.o: cli/stacktrace.cpp cli/stacktrace.h lib/config.h lib/utils.h
$(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ cli/stacktrace.cpp

cli/threadexecutor.o: cli/threadexecutor.cpp cli/executor.h cli/threadexecutor.h lib/addoninfo.h lib/check.h lib/checkers.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/timer.h lib/utils.h
cli/threadexecutor.o: cli/threadexecutor.cpp cli/executor.h cli/threadexecutor.h lib/addoninfo.h lib/check.h lib/checkers.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h
$(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ cli/threadexecutor.cpp

test/fixture.o: test/fixture.cpp externals/simplecpp/simplecpp.h externals/tinyxml2/tinyxml2.h lib/addoninfo.h lib/check.h lib/checkers.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/xml.h test/fixture.h test/helpers.h test/options.h test/redirect.h
Expand Down
6 changes: 6 additions & 0 deletions cli/cppcheckexecutor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,12 @@ bool CppCheckExecutor::reportSuppressions(const Settings &settings, const Suppre
suppressions.getUnmatchedLocalSuppressions(i->file, unusedFunctionCheckEnabled), errorLogger);
}
}
if (settings.inlineSuppressions) {
// report unmatched unusedFunction suppressions
err |= SuppressionList::reportUnmatchedSuppressions(
suppressions.getUnmatchedInlineSuppressions(), errorLogger);
}

err |= SuppressionList::reportUnmatchedSuppressions(suppressions.getUnmatchedGlobalSuppressions(unusedFunctionCheckEnabled), errorLogger);
return err;
}
Expand Down
57 changes: 53 additions & 4 deletions cli/processexecutor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ ProcessExecutor::ProcessExecutor(const std::list<FileWithDetails> &files, const
namespace {
class PipeWriter : public ErrorLogger {
public:
enum PipeSignal : std::uint8_t {REPORT_OUT='1',REPORT_ERROR='2', CHILD_END='5'};
enum PipeSignal : std::uint8_t {REPORT_OUT='1',REPORT_ERROR='2',REPORT_SUPPR_INLINE='3',CHILD_END='5'};

explicit PipeWriter(int pipe) : mWpipe(pipe) {}

Expand All @@ -90,11 +90,32 @@ namespace {
writeToPipe(REPORT_ERROR, msg.serialize());
}

void writeSuppr(const SuppressionList &supprs) const {
for (const auto& suppr : supprs.getSuppressions())
{
if (!suppr.isInline)
continue;

writeToPipe(REPORT_SUPPR_INLINE, suppressionToString(suppr));
}
// TODO: update suppression states?
}

void writeEnd(const std::string& str) const {
writeToPipe(CHILD_END, str);
}

private:
static std::string suppressionToString(const SuppressionList::Suppression &suppr)
{
std::string suppr_str = suppr.toString();
suppr_str += ";";
suppr_str += suppr.checked ? "1" : "0";
suppr_str += ";";
suppr_str += suppr.matched ? "1" : "0";
return suppr_str;
}

// TODO: how to log file name in error?
void writeToPipeInternal(PipeSignal type, const void* data, std::size_t to_write) const
{
Expand Down Expand Up @@ -124,7 +145,7 @@ namespace {
writeToPipeInternal(type, &len, l_size);
}

if (len > 0)
if (len > 0) // TODO: unexpected - write a warning?
writeToPipeInternal(type, data.c_str(), len);
}

Expand Down Expand Up @@ -155,7 +176,10 @@ bool ProcessExecutor::handleRead(int rpipe, unsigned int &result, const std::str
std::exit(EXIT_FAILURE);
}

if (type != PipeWriter::REPORT_OUT && type != PipeWriter::REPORT_ERROR && type != PipeWriter::CHILD_END) {
if (type != PipeWriter::REPORT_OUT &&
type != PipeWriter::REPORT_ERROR &&
type != PipeWriter::REPORT_SUPPR_INLINE &&
type != PipeWriter::CHILD_END) {
std::cerr << "#### ThreadExecutor::handleRead(" << filename << ") invalid type " << int(type) << std::endl;
std::exit(EXIT_FAILURE);
}
Expand All @@ -174,7 +198,7 @@ bool ProcessExecutor::handleRead(int rpipe, unsigned int &result, const std::str
}

std::string buf(len, '\0');
if (len > 0) {
if (len > 0) { // TODO: unexpected - write a warning?
char *data_start = &buf[0];
bytes_to_read = len;
do {
Expand Down Expand Up @@ -206,6 +230,29 @@ bool ProcessExecutor::handleRead(int rpipe, unsigned int &result, const std::str

if (hasToLog(msg))
mErrorLogger.reportErr(msg);
} else if (type == PipeWriter::REPORT_SUPPR_INLINE) {
if (!buf.empty()) {
// TODO: avoid string splitting
auto parts = splitString(buf, ';');
if (parts.size() != 3)
{
// TODO: make this non-fatal
std::cerr << "#### ThreadExecutor::handleRead(" << filename << ") adding of inline suppression failed - insufficient data" << std::endl;
std::exit(EXIT_FAILURE);
}
auto suppr = SuppressionList::parseLine(parts[0]);
suppr.isInline = true;
suppr.checked = parts[1] == "1";
suppr.matched = parts[2] == "1";
const std::string err = mSuppressions.nomsg.addSuppression(suppr);
if (!err.empty()) {
// TODO: only update state if it doesn't exist - otherwise propagate error
mSuppressions.nomsg.updateSuppressionState(suppr); // TODO: check result
// TODO: make this non-fatal
//std::cerr << "#### ThreadExecutor::handleRead(" << filename << ") adding of inline suppression failed - " << err << std::endl;
//std::exit(EXIT_FAILURE);
}
}
} else if (type == PipeWriter::CHILD_END) {
result += std::stoi(buf);
res = false;
Expand Down Expand Up @@ -297,6 +344,8 @@ unsigned int ProcessExecutor::check()
// TODO: call analyseClangTidy()?
}

pipewriter.writeSuppr(mSuppressions.nomsg);

pipewriter.writeEnd(std::to_string(resultOfCheck));
std::exit(EXIT_SUCCESS);
}
Expand Down
18 changes: 18 additions & 0 deletions cli/threadexecutor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "errorlogger.h"
#include "filesettings.h"
#include "settings.h"
#include "suppressions.h"
#include "timer.h"

#include <cassert>
Expand Down Expand Up @@ -125,6 +126,23 @@ class ThreadData
result = fileChecker.check(*file);
// TODO: call analyseClangTidy()?
}
for (const auto& suppr : mSuppressions.nomsg.getSuppressions()) {
// need to transfer all inline suppressions because these are used later on
if (suppr.isInline) {
const std::string err = mSuppressions.nomsg.addSuppression(suppr);
if (!err.empty()) {
// TODO: only update state if it doesn't exist - otherwise propagate error
mSuppressions.nomsg.updateSuppressionState(suppr); // TODO: check result
}
continue;
}

// propagate state of global suppressions
if (!suppr.isLocal()) {
mSuppressions.nomsg.updateSuppressionState(suppr); // TODO: check result
continue;
}
}
return result;
}

Expand Down
18 changes: 12 additions & 6 deletions lib/cppcheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1168,8 +1168,10 @@ unsigned int CppCheck::checkFile(const FileWithDetails& file, const std::string
fdump << "</dump>" << std::endl;
}

// Need to call this even if the hash will skip this configuration
mSuppressions.nomsg.markUnmatchedInlineSuppressionsAsChecked(tokenizer);
if (mSettings.inlineSuppressions) {
// Need to call this even if the hash will skip this configuration
mSuppressions.nomsg.markUnmatchedInlineSuppressionsAsChecked(tokenizer);
}

// Skip if we already met the same simplified token list
if (mSettings.force || mSettings.maxConfigs > 1) {
Expand Down Expand Up @@ -1258,10 +1260,14 @@ unsigned int CppCheck::checkFile(const FileWithDetails& file, const std::string
mErrorLogger.reportErr(errmsg);
}

// In jointSuppressionReport mode, unmatched suppressions are
// collected after all files are processed
if (!mSettings.useSingleJob() && (mSettings.severity.isEnabled(Severity::information) || mSettings.checkConfiguration)) {
SuppressionList::reportUnmatchedSuppressions(mSuppressions.nomsg.getUnmatchedLocalSuppressions(file, (bool)mUnusedFunctionsCheck), mErrorLogger);
// TODO: this is done too early causing the whole program analysis suppressions to be reported as unmatched
if (mSettings.severity.isEnabled(Severity::information) || mSettings.checkConfiguration) {
// In jointSuppressionReport mode, unmatched suppressions are
// collected after all files are processed
if (!mSettings.useSingleJob()) {
// TODO: check result?
SuppressionList::reportUnmatchedSuppressions(mSuppressions.nomsg.getUnmatchedLocalSuppressions(file, (bool)mUnusedFunctionsCheck), mErrorLogger);
}
}

if (analyzerInformation) {
Expand Down
2 changes: 2 additions & 0 deletions lib/preprocessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ static bool parseInlineSuppressionCommentToken(const simplecpp::Token *tok, std:
std::vector<SuppressionList::Suppression> suppressions = SuppressionList::parseMultiSuppressComment(comment, &errmsg);

for (SuppressionList::Suppression &s : suppressions) {
s.isInline = true;
s.type = errorType;
s.lineNumber = tok->location.line;
}
Expand All @@ -152,6 +153,7 @@ static bool parseInlineSuppressionCommentToken(const simplecpp::Token *tok, std:
if (!s.parseComment(comment, &errmsg))
return false;

s.isInline = true;
s.type = errorType;
s.lineNumber = tok->location.line;

Expand Down
38 changes: 32 additions & 6 deletions lib/suppressions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ std::vector<SuppressionList::Suppression> SuppressionList::parseMultiSuppressCom
return suppressions;
}

std::string SuppressionList::addSuppressionLine(const std::string &line)
SuppressionList::Suppression SuppressionList::parseLine(const std::string &line)
{
std::istringstream lineStream;
SuppressionList::Suppression suppression;
Expand Down Expand Up @@ -249,7 +249,12 @@ std::string SuppressionList::addSuppressionLine(const std::string &line)

suppression.fileName = Path::simplifyPath(suppression.fileName);

return addSuppression(std::move(suppression));
return suppression;
}

std::string SuppressionList::addSuppressionLine(const std::string &line)
{
return addSuppression(parseLine(line));
}

std::string SuppressionList::addSuppression(SuppressionList::Suppression suppression)
Expand Down Expand Up @@ -511,12 +516,14 @@ void SuppressionList::dump(std::ostream & out) const
out << " </suppressions>" << std::endl;
}

std::list<SuppressionList::Suppression> SuppressionList::getUnmatchedLocalSuppressions(const FileWithDetails &file, const bool unusedFunctionChecking) const
std::list<SuppressionList::Suppression> SuppressionList::getUnmatchedLocalSuppressions(const FileWithDetails &file, const bool includeUnusedFunction) const
{
std::lock_guard<std::mutex> lg(mSuppressionsSync);

std::list<Suppression> result;
for (const Suppression &s : mSuppressions) {
if (s.isInline)
continue;
if (s.matched || ((s.lineNumber != Suppression::NO_LINE) && !s.checked))
continue;
if (s.type == SuppressionList::Type::macro)
Expand All @@ -525,7 +532,7 @@ std::list<SuppressionList::Suppression> SuppressionList::getUnmatchedLocalSuppre
continue;
if (s.errorId == ID_CHECKERSREPORT)
continue;
if (!unusedFunctionChecking && s.errorId == ID_UNUSEDFUNCTION)
if (!includeUnusedFunction && s.errorId == ID_UNUSEDFUNCTION)
continue;
if (!s.isLocal() || s.fileName != file.spath())
continue;
Expand All @@ -534,17 +541,19 @@ std::list<SuppressionList::Suppression> SuppressionList::getUnmatchedLocalSuppre
return result;
}

std::list<SuppressionList::Suppression> SuppressionList::getUnmatchedGlobalSuppressions(const bool unusedFunctionChecking) const
std::list<SuppressionList::Suppression> SuppressionList::getUnmatchedGlobalSuppressions(const bool includeUnusedFunction) const
{
std::lock_guard<std::mutex> lg(mSuppressionsSync);

std::list<Suppression> result;
for (const Suppression &s : mSuppressions) {
if (s.isInline)
continue;
if (s.matched || ((s.lineNumber != Suppression::NO_LINE) && !s.checked))
continue;
if (s.hash > 0)
continue;
if (!unusedFunctionChecking && s.errorId == ID_UNUSEDFUNCTION)
if (!includeUnusedFunction && s.errorId == ID_UNUSEDFUNCTION)
continue;
if (s.errorId == ID_CHECKERSREPORT)
continue;
Expand All @@ -555,6 +564,23 @@ std::list<SuppressionList::Suppression> SuppressionList::getUnmatchedGlobalSuppr
return result;
}

std::list<SuppressionList::Suppression> SuppressionList::getUnmatchedInlineSuppressions() const
{
std::list<SuppressionList::Suppression> result;
for (const SuppressionList::Suppression &s : SuppressionList::mSuppressions) {
if (!s.isInline)
continue;
if (!s.checked)
continue;
if (s.matched)
continue;
if (s.hash > 0)
continue;
result.push_back(s);
}
return result;
}

std::list<SuppressionList::Suppression> SuppressionList::getSuppressions() const
{
std::lock_guard<std::mutex> lg(mSuppressionsSync);
Expand Down
18 changes: 16 additions & 2 deletions lib/suppressions.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ class CPPCHECKLIB SuppressionList {
bool thisAndNextLine{}; // Special case for backwards compatibility: { // cppcheck-suppress something
bool matched{};
bool checked{}; // for inline suppressions, checked or not
bool isInline{};

enum : std::int8_t { NO_LINE = -1 };
};
Expand All @@ -177,6 +178,13 @@ class CPPCHECKLIB SuppressionList {
*/
static std::vector<Suppression> parseMultiSuppressComment(const std::string &comment, std::string *errorMessage);

/**
* Create a Suppression object from a suppression line
* @param line The line to parse.
* @return a suppression object
*/
static Suppression parseLine(const std::string &line);

/**
* @brief Don't show the given error.
* @param line Description of error to suppress (in id:file:line format).
Expand Down Expand Up @@ -239,13 +247,19 @@ class CPPCHECKLIB SuppressionList {
* @brief Returns list of unmatched local (per-file) suppressions.
* @return list of unmatched suppressions
*/
std::list<Suppression> getUnmatchedLocalSuppressions(const FileWithDetails &file, bool unusedFunctionChecking) const;
std::list<Suppression> getUnmatchedLocalSuppressions(const FileWithDetails &file, bool includeUnusedFunction) const;

/**
* @brief Returns list of unmatched global (glob pattern) suppressions.
* @return list of unmatched suppressions
*/
std::list<Suppression> getUnmatchedGlobalSuppressions(bool unusedFunctionChecking) const;
std::list<Suppression> getUnmatchedGlobalSuppressions(bool includeUnusedFunction) const;

/**
* @brief Returns list of unmatched inline suppressions.
* @return list of unmatched suppressions
*/
std::list<Suppression> getUnmatchedInlineSuppressions() const;

/**
* @brief Returns list of all suppressions.
Expand Down
Loading
Loading