Skip to content

Commit a7a8038

Browse files
authored
Fix #13216: Add checkers-report block to xml output (#6914)
1 parent 5c09d93 commit a7a8038

8 files changed

+115
-14
lines changed

cli/cmdlineparser.cpp

+4-4
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a
340340
return Result::Fail;
341341
{
342342
XMLErrorMessagesLogger xmlLogger;
343-
std::cout << ErrorMessage::getXMLHeader(mSettings.cppcheckCfgProductName);
343+
std::cout << ErrorMessage::getXMLHeader(mSettings.cppcheckCfgProductName, mSettings.xml_version);
344344
CppCheck::getErrorMessages(xmlLogger);
345345
std::cout << ErrorMessage::getXMLFooter() << std::endl;
346346
}
@@ -1392,9 +1392,9 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a
13921392
int tmp;
13931393
if (!parseNumberArg(argv[i], 14, tmp))
13941394
return Result::Fail;
1395-
if (tmp != 2) {
1396-
// We only have xml version 2
1397-
mLogger.printError("'--xml-version' can only be 2.");
1395+
if (tmp != 2 && tmp != 3) {
1396+
// We only have xml version 2 and 3
1397+
mLogger.printError("'--xml-version' can only be 2 or 3.");
13981398
return Result::Fail;
13991399
}
14001400

cli/cppcheckexecutor.cpp

+7-2
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,7 @@ int CppCheckExecutor::check_internal(const Settings& settings) const
413413
stdLogger.resetLatestProgressOutputTime();
414414

415415
if (settings.xml) {
416-
stdLogger.reportErr(ErrorMessage::getXMLHeader(settings.cppcheckCfgProductName));
416+
stdLogger.reportErr(ErrorMessage::getXMLHeader(settings.cppcheckCfgProductName, settings.xml_version));
417417
}
418418

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

468468
if (settings.xml) {
469-
stdLogger.reportErr(ErrorMessage::getXMLFooter());
469+
stdLogger.reportErr(ErrorMessage::getXMLFooter(settings.xml_version));
470470
}
471471

472472
if (settings.safety && stdLogger.hasCriticalErrors())
@@ -512,6 +512,11 @@ void StdLogger::writeCheckersReport()
512512
if (fout.is_open())
513513
fout << checkersReport.getReport(mCriticalErrors);
514514
}
515+
516+
if (mSettings.xml && mSettings.xml_version == 3) {
517+
reportErr(" </errors>\n");
518+
reportErr(checkersReport.getXmlReport(mCriticalErrors));
519+
}
515520
}
516521

517522
#ifdef _WIN32

lib/checkersreport.cpp

+85
Original file line numberDiff line numberDiff line change
@@ -280,3 +280,88 @@ std::string CheckersReport::getReport(const std::string& criticalErrors) const
280280

281281
return fout.str();
282282
}
283+
284+
std::string CheckersReport::getXmlReport(const std::string& criticalErrors) const
285+
{
286+
std::string ret;
287+
288+
if (!criticalErrors.empty()) {
289+
ret += " <critical-errors>" + criticalErrors + "\n </critical-errors>\n";
290+
} else
291+
ret += " <critical-errors/>\n";
292+
ret += " <checkers-report>\n";
293+
294+
const bool cppcheckPremium = isCppcheckPremium(mSettings);
295+
296+
auto reportSection = [&ret, cppcheckPremium]
297+
(const std::string& title,
298+
const Settings& settings,
299+
const std::set<std::string>& activeCheckers,
300+
const std::map<std::string, std::string>& premiumCheckers,
301+
const std::string& substring) {
302+
if (!cppcheckPremium) {
303+
ret += "<" + title + "/>\n";
304+
return;
305+
}
306+
ret += " <" + title + ">\n";
307+
for (const auto& checkReq: premiumCheckers) {
308+
const std::string& checker = checkReq.first;
309+
if (checker.find(substring) == std::string::npos)
310+
continue;
311+
bool active = cppcheckPremium && activeCheckers.count(checker) > 0;
312+
if (substring == "::") {
313+
if (checkReq.second == "warning")
314+
active &= settings.severity.isEnabled(Severity::warning);
315+
else if (checkReq.second == "style")
316+
active &= settings.severity.isEnabled(Severity::style);
317+
else if (checkReq.second == "portability")
318+
active &= settings.severity.isEnabled(Severity::portability);
319+
else if (!checkReq.second.empty())
320+
active = false; // FIXME: handle req
321+
}
322+
ret += " <checker active=\"" + std::string(active ? "Yes" : "No") + "\" id=\"" + checker + "\"";
323+
ret += "/>\n";
324+
}
325+
ret += " </" + title + ">\n";
326+
};
327+
328+
reportSection("premium-checkers", mSettings, mActiveCheckers, checkers::premiumCheckers, "::");
329+
reportSection("autosar", mSettings, mActiveCheckers, checkers::premiumCheckers, "Autosar: ");
330+
reportSection("cert-c", mSettings, mActiveCheckers, checkers::premiumCheckers, "Cert C: ");
331+
reportSection("cert-cpp", mSettings, mActiveCheckers, checkers::premiumCheckers, "Cert C++: ");
332+
333+
int misra = 0;
334+
if (mSettings.premiumArgs.find("misra-c-2012") != std::string::npos)
335+
misra = 2012;
336+
else if (mSettings.premiumArgs.find("misra-c-2023") != std::string::npos)
337+
misra = 2023;
338+
else if (mSettings.addons.count("misra"))
339+
misra = 2012;
340+
341+
if (misra == 0) {
342+
ret += " <misra-c/>\n";
343+
} else {
344+
ret += " <misra-c-" + std::to_string(misra) + ">\n";
345+
for (const checkers::MisraInfo& info: checkers::misraC2012Directives) {
346+
const std::string directive = "Dir " + std::to_string(info.a) + "." + std::to_string(info.b);
347+
const bool active = isMisraRuleActive(mActiveCheckers, directive);
348+
ret += " <checker active=\"";
349+
ret += std::string(active ? "Yes" : "No") + "\" id=\"Misra C " + std::to_string(misra) + ": " + directive + "\"";
350+
ret += "/>\n";
351+
}
352+
for (const checkers::MisraInfo& info: checkers::misraC2012Rules) {
353+
const std::string rule = std::to_string(info.a) + "." + std::to_string(info.b);
354+
const bool active = isMisraRuleActive(mActiveCheckers, rule);
355+
ret += " <checker active=\"";
356+
ret += std::string(active ? "Yes" : "No") + "\" id=\"Misra C " + std::to_string(misra) + ": " + rule + "\"";
357+
ret += "/>\n";
358+
}
359+
ret += " </misra-c-" + std::to_string(misra) + ">\n";
360+
}
361+
362+
reportSection("misra-cpp-2008", mSettings, mActiveCheckers, checkers::premiumCheckers, "Misra C++ 2008: ");
363+
reportSection("misra-cpp-2023", mSettings, mActiveCheckers, checkers::premiumCheckers, "Misra C++ 2023: ");
364+
365+
ret += " </checkers-report>";
366+
return ret;
367+
}

lib/checkersreport.h

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class CPPCHECKLIB CheckersReport {
3434
int getAllCheckersCount();
3535

3636
std::string getReport(const std::string& criticalErrors) const;
37+
std::string getXmlReport(const std::string& criticalErrors) const;
3738

3839
private:
3940
const Settings& mSettings;

lib/errorlogger.cpp

+4-4
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,7 @@ void ErrorMessage::deserialize(const std::string &data)
423423
}
424424
}
425425

426-
std::string ErrorMessage::getXMLHeader(std::string productName)
426+
std::string ErrorMessage::getXMLHeader(std::string productName, int xmlVersion)
427427
{
428428
const auto nameAndVersion = Settings::getNameAndVersion(productName);
429429
productName = nameAndVersion.first;
@@ -437,7 +437,7 @@ std::string ErrorMessage::getXMLHeader(std::string productName)
437437
// header
438438
printer.OpenElement("results", false);
439439

440-
printer.PushAttribute("version", 2);
440+
printer.PushAttribute("version", xmlVersion);
441441
printer.OpenElement("cppcheck", false);
442442
if (!productName.empty())
443443
printer.PushAttribute("product-name", productName.c_str());
@@ -448,9 +448,9 @@ std::string ErrorMessage::getXMLHeader(std::string productName)
448448
return std::string(printer.CStr()) + '>';
449449
}
450450

451-
std::string ErrorMessage::getXMLFooter()
451+
std::string ErrorMessage::getXMLFooter(int xmlVersion)
452452
{
453-
return " </errors>\n</results>";
453+
return xmlVersion == 3? "</results>" : " </errors>\n</results>";
454454
}
455455

456456
// There is no utf-8 support around but the strings should at least be safe for to tinyxml2.

lib/errorlogger.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,8 @@ class CPPCHECKLIB ErrorMessage {
142142
*/
143143
std::string toXML() const;
144144

145-
static std::string getXMLHeader(std::string productName);
146-
static std::string getXMLFooter();
145+
static std::string getXMLHeader(std::string productName, int xmlVersion = 2);
146+
static std::string getXMLFooter(int xmlVersion = 2);
147147

148148
/**
149149
* Format the error message into a string.

test/cli/helloworld_test.py

+10
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import re
66
import glob
77
import json
8+
import xml.etree.ElementTree as ET
89

910
from testutils import create_gui_project_file, cppcheck
1011

@@ -338,3 +339,12 @@ def test_sarif():
338339
assert res['runs'][0]['tool']['driver']['rules'][0]['properties']['security-severity'] > 9.5
339340
assert 'security' in res['runs'][0]['tool']['driver']['rules'][0]['properties']['tags']
340341
assert re.match(r'[0-9]+(.[0-9]+)+', res['runs'][0]['tool']['driver']['semanticVersion'])
342+
343+
344+
def test_xml_checkers_report():
345+
test_file = os.path.join(__proj_dir, 'main.c')
346+
args = ['--xml-version=3', '--enable=all', test_file]
347+
348+
exitcode, _, stderr = cppcheck(args)
349+
assert exitcode == 0
350+
assert ET.fromstring(stderr) is not None

test/testcmdlineparser.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -1809,10 +1809,10 @@ class TestCmdlineParser : public TestFixture {
18091809

18101810
void xmlverunknown() {
18111811
REDIRECT;
1812-
const char * const argv[] = {"cppcheck", "--xml", "--xml-version=3", "file.cpp"};
1812+
const char * const argv[] = {"cppcheck", "--xml", "--xml-version=4", "file.cpp"};
18131813
// FAils since unknown XML format version
18141814
ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(4, argv));
1815-
ASSERT_EQUALS("cppcheck: error: '--xml-version' can only be 2.\n", logger->str());
1815+
ASSERT_EQUALS("cppcheck: error: '--xml-version' can only be 2 or 3.\n", logger->str());
18161816
}
18171817

18181818
void xmlverinvalid() {

0 commit comments

Comments
 (0)