Skip to content

Commit

Permalink
Fix #13216: Add checkers-report block to xml output
Browse files Browse the repository at this point in the history
  • Loading branch information
olabetskyi committed Oct 14, 2024
1 parent 51df244 commit 9497565
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 12 deletions.
8 changes: 4 additions & 4 deletions cli/cmdlineparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a
return Result::Fail;
{
XMLErrorMessagesLogger xmlLogger;
std::cout << ErrorMessage::getXMLHeader(mSettings.cppcheckCfgProductName);
std::cout << ErrorMessage::getXMLHeader(mSettings.cppcheckCfgProductName, mSettings.xml_version);
CppCheck::getErrorMessages(xmlLogger);
std::cout << ErrorMessage::getXMLFooter() << std::endl;
}
Expand Down Expand Up @@ -1386,9 +1386,9 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a
int tmp;
if (!parseNumberArg(argv[i], 14, tmp))
return Result::Fail;
if (tmp != 2) {
// We only have xml version 2
mLogger.printError("'--xml-version' can only be 2.");
if (tmp != 2 && tmp != 3) {
// We only have xml version 2 and 3
mLogger.printError("'--xml-version' can only be 2 or 3.");
return Result::Fail;
}

Expand Down
9 changes: 7 additions & 2 deletions cli/cppcheckexecutor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ int CppCheckExecutor::check_internal(const Settings& settings) const
stdLogger.resetLatestProgressOutputTime();

if (settings.xml) {
stdLogger.reportErr(ErrorMessage::getXMLHeader(settings.cppcheckCfgProductName));
stdLogger.reportErr(ErrorMessage::getXMLHeader(settings.cppcheckCfgProductName, settings.xml_version));
}

if (!settings.buildDir.empty()) {
Expand Down Expand Up @@ -466,7 +466,7 @@ int CppCheckExecutor::check_internal(const Settings& settings) const
stdLogger.writeCheckersReport();

if (settings.xml) {
stdLogger.reportErr(ErrorMessage::getXMLFooter());
stdLogger.reportErr(ErrorMessage::getXMLFooter(settings.xml_version));
}

if (settings.safety && stdLogger.hasCriticalErrors())
Expand Down Expand Up @@ -512,6 +512,11 @@ void StdLogger::writeCheckersReport()
if (fout.is_open())
fout << checkersReport.getReport(mCriticalErrors);
}

if (mSettings.xml && mSettings.xml_version == 3) {
reportErr(" </errors>\n");
reportErr(checkersReport.getXmlReport(mCriticalErrors));
}
}

#ifdef _WIN32
Expand Down
136 changes: 136 additions & 0 deletions lib/checkersreport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -280,3 +280,139 @@ std::string CheckersReport::getReport(const std::string& criticalErrors) const

return fout.str();
}

std::string CheckersReport::getXmlReport(const std::string& criticalErrors) const
{
std::ostringstream fout;

fout << " <critical errors>";
if (!criticalErrors.empty())
fout << criticalErrors << std::endl;
fout << "</critical errors>" << std::endl;

fout << " <checkers-report>" << std::endl;
fout << " <open source checkers>" << std::endl;

std::size_t maxCheckerSize = 0;
for (const auto& checkReq: checkers::allCheckers) {
const std::string& checker = checkReq.first;
maxCheckerSize = std::max(checker.size(), maxCheckerSize);
}
for (const auto& checkReq: checkers::allCheckers) {
fout << " <checker active=\"";
const std::string& checker = checkReq.first;
const bool active = mActiveCheckers.count(checkReq.first) > 0;
const std::string& req = checkReq.second;
fout << (active ? "Yes" : "No") << "\"" << " id=\"" << checker << "\"";
if (!active && !req.empty())
fout << " require=\"" + req << "\"";
fout << "/>" << std::endl;
}

fout << " </open source checkers>" << std::endl;

const bool cppcheckPremium = isCppcheckPremium(mSettings);

auto reportSection = [&fout, cppcheckPremium]
(const std::string& title,
const Settings& settings,
const std::set<std::string>& activeCheckers,
const std::map<std::string, std::string>& premiumCheckers,
const std::string& substring) {
fout << " <" << title << ">";
if (!cppcheckPremium) {
fout << "Not available, Cppcheck Premium is not used</" << title << ">" << std::endl;
return;
}
fout << std::endl;
int maxCheckerSize = 0;
for (const auto& checkReq: premiumCheckers) {
const std::string& checker = checkReq.first;
if (checker.find(substring) != std::string::npos && checker.size() > maxCheckerSize)
maxCheckerSize = checker.size();
}
for (const auto& checkReq: premiumCheckers) {
const std::string& checker = checkReq.first;
if (checker.find(substring) == std::string::npos)
continue;
std::string req = checkReq.second;
bool active = cppcheckPremium && activeCheckers.count(checker) > 0;
if (substring == "::") {
if (req == "warning")
active &= settings.severity.isEnabled(Severity::warning);
else if (req == "style")
active &= settings.severity.isEnabled(Severity::style);
else if (req == "portability")
active &= settings.severity.isEnabled(Severity::portability);
else if (!req.empty())
active = false; // FIXME: handle req
}
fout << " <checker active=\"" << (active ? "Yes" : "No") << "\" id=\"" << checker << "\"";
if (!cppcheckPremium) {
if (!req.empty())
req = "premium," + req;
else
req = "premium";
}
if (!active)
fout << " require=\"" << req << "\"";
fout << "/>" << std::endl;
}
fout << " </" << title << ">" << std::endl;
};

reportSection("Premium Checkers", mSettings, mActiveCheckers, checkers::premiumCheckers, "::");
reportSection("Autosar", mSettings, mActiveCheckers, checkers::premiumCheckers, "Autosar: ");
reportSection("Cert C", mSettings, mActiveCheckers, checkers::premiumCheckers, "Cert C: ");
reportSection("Cert Cpp", mSettings, mActiveCheckers, checkers::premiumCheckers, "Cert C++: ");

int misra = 0;
if (mSettings.premiumArgs.find("misra-c-2012") != std::string::npos)
misra = 2012;
else if (mSettings.premiumArgs.find("misra-c-2023") != std::string::npos)
misra = 2023;
else if (mSettings.addons.count("misra"))
misra = 2012;

if (misra == 0) {
fout << " <Misra C> Misra is not enabled </Misra C>" << std::endl;
} else {
fout << " <Misra C " << misra << ">" << std::endl;
for (const checkers::MisraInfo& info: checkers::misraC2012Directives) {
const std::string directive = "Dir " + std::to_string(info.a) + "." + std::to_string(info.b);
const bool active = isMisraRuleActive(mActiveCheckers, directive);
fout << " <checker active=\"";
fout << (active ? "Yes" : "No") << "\" id=\"" << "Misra C " << misra << ": " << directive << "\"";
std::string extra;
if (misra == 2012 && info.amendment >= 1)
extra = " amendment=\"" + std::to_string(info.amendment) + "\"";
if (!extra.empty())
fout << extra;
fout << "/>" << std::endl;
}
for (const checkers::MisraInfo& info: checkers::misraC2012Rules) {
const std::string rule = std::to_string(info.a) + "." + std::to_string(info.b);
const bool active = isMisraRuleActive(mActiveCheckers, rule);
fout << " <checker active=\"";
fout << (active ? "Yes" : "No") << "\" id=\"" << "Misra C " << misra << ": " << rule << "\"";
std::string extra;
if (misra == 2012 && info.amendment >= 1)
extra = " amendment=\"" + std::to_string(info.amendment) + "\"";
std::string reqs;
if (info.amendment >= 3)
reqs += "premium";
if (!active && !reqs.empty())
extra += " require=\"" + reqs + "\"";
if (!extra.empty())
fout << extra;
fout << "/>" << std::endl;
}
fout << " </Misra C " << misra << ">" << std::endl;
}

reportSection("Misra C++ 2008", mSettings, mActiveCheckers, checkers::premiumCheckers, "Misra C++ 2008: ");
reportSection("Misra C++ 2023", mSettings, mActiveCheckers, checkers::premiumCheckers, "Misra C++ 2023: ");

fout << " </checkers-report>";
return fout.str();
}
1 change: 1 addition & 0 deletions lib/checkersreport.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class CPPCHECKLIB CheckersReport {
int getAllCheckersCount();

std::string getReport(const std::string& criticalErrors) const;
std::string getXmlReport(const std::string& criticalErrors) const;

private:
const Settings& mSettings;
Expand Down
8 changes: 4 additions & 4 deletions lib/errorlogger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ void ErrorMessage::deserialize(const std::string &data)
}
}

std::string ErrorMessage::getXMLHeader(std::string productName)
std::string ErrorMessage::getXMLHeader(std::string productName, int _xmlVersion)
{
const auto nameAndVersion = Settings::getNameAndVersion(productName);
productName = nameAndVersion.first;
Expand All @@ -437,7 +437,7 @@ std::string ErrorMessage::getXMLHeader(std::string productName)
// header
printer.OpenElement("results", false);

printer.PushAttribute("version", 2);
printer.PushAttribute("version", _xmlVersion);
printer.OpenElement("cppcheck", false);
if (!productName.empty())
printer.PushAttribute("product-name", productName.c_str());
Expand All @@ -448,9 +448,9 @@ std::string ErrorMessage::getXMLHeader(std::string productName)
return std::string(printer.CStr()) + '>';
}

std::string ErrorMessage::getXMLFooter()
std::string ErrorMessage::getXMLFooter(int _xmlVersion)
{
return " </errors>\n</results>";
return _xmlVersion == 3? "</results>" : " </errors>\n</results>";
}

// There is no utf-8 support around but the strings should at least be safe for to tinyxml2.
Expand Down
4 changes: 2 additions & 2 deletions lib/errorlogger.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,8 @@ class CPPCHECKLIB ErrorMessage {
*/
std::string toXML() const;

static std::string getXMLHeader(std::string productName);
static std::string getXMLFooter();
static std::string getXMLHeader(std::string productName, int _xmlVersion = 2);
static std::string getXMLFooter(int _xmlVersion = 2);

/**
* Format the error message into a string.
Expand Down

0 comments on commit 9497565

Please sign in to comment.