cppcheck/lib/checkersreport.cpp

232 lines
8.9 KiB
C++

/*
* Cppcheck - A tool for static C/C++ code analysis
* Copyright (C) 2007-2023 Cppcheck team.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "checkersreport.h"
#include "checkers.h"
#include "errortypes.h"
#include "settings.h"
#include <map>
#include <sstream>
#include <unordered_set>
#include <utility>
#include <vector>
static bool isCppcheckPremium(const Settings& settings) {
return (settings.cppcheckCfgProductName.compare(0, 16, "Cppcheck Premium") == 0);
}
static std::string getMisraRuleSeverity(const std::string& rule) {
if (checkers::misraRuleSeverity.count(rule) > 0)
return checkers::misraRuleSeverity.at(rule);
return "style";
}
static bool isMisraRuleInconclusive(const std::string& rule) {
return rule == "8.3";
}
static bool isMisraRuleActive(const std::string& rule, int amendment, const std::string& severity, const Settings& settings) {
if (!isCppcheckPremium(settings) && amendment >= 3)
return false;
const bool inconclusive = isMisraRuleInconclusive(rule);
if (inconclusive && !settings.certainty.isEnabled(Certainty::inconclusive))
return false;
if (severity == "warning")
return settings.severity.isEnabled(Severity::warning);
if (severity == "style")
return settings.severity.isEnabled(Severity::style);
return true; // error severity
}
CheckersReport::CheckersReport(const Settings& settings, const std::set<std::string>& activeCheckers)
: mSettings(settings), mActiveCheckers(activeCheckers)
{}
int CheckersReport::getActiveCheckersCount()
{
if (mAllCheckersCount == 0) {
countCheckers();
}
return mActiveCheckersCount;
}
int CheckersReport::getAllCheckersCount()
{
if (mAllCheckersCount == 0) {
countCheckers();
}
return mAllCheckersCount;
}
void CheckersReport::countCheckers()
{
mActiveCheckersCount = mAllCheckersCount = 0;
for (const auto& checkReq: checkers::allCheckers) {
if (mActiveCheckers.count(checkReq.first) > 0)
++mActiveCheckersCount;
++mAllCheckersCount;
}
for (const auto& checkReq: checkers::premiumCheckers) {
if (mActiveCheckers.count(checkReq.first) > 0)
++mActiveCheckersCount;
++mAllCheckersCount;
}
if (mSettings.premiumArgs.find("misra-c-") != std::string::npos || mSettings.addons.count("misra")) {
for (const checkers::MisraInfo& info: checkers::misraC2012Rules) {
const std::string rule = std::to_string(info.a) + "." + std::to_string(info.b);
const std::string severity = getMisraRuleSeverity(rule);
const bool active = isMisraRuleActive(rule, info.amendment, severity, mSettings);
if (active)
++mActiveCheckersCount;
++mAllCheckersCount;
}
}
}
std::string CheckersReport::getReport(const std::string& criticalErrors) const
{
std::ostringstream fout;
fout << "Critical errors" << std::endl;
fout << "---------------" << std::endl;
if (!criticalErrors.empty()) {
fout << "There was critical errors (" << criticalErrors << ")" << std::endl;
fout << "All checking is skipped for a file with such error" << std::endl;
} else {
fout << "No critical errors, all files were checked." << std::endl;
fout << "Important: Analysis is still not guaranteed to be 'complete' it is possible there are false negatives." << std::endl;
}
fout << std::endl << std::endl;
fout << "Open source checkers" << std::endl;
fout << "--------------------" << std::endl;
int maxCheckerSize = 0;
for (const auto& checkReq: checkers::allCheckers) {
const std::string& checker = checkReq.first;
if (checker.size() > maxCheckerSize)
maxCheckerSize = checker.size();
}
for (const auto& checkReq: checkers::allCheckers) {
const std::string& checker = checkReq.first;
const bool active = mActiveCheckers.count(checkReq.first) > 0;
const std::string& req = checkReq.second;
fout << (active ? "Yes " : "No ") << checker;
if (!active && !req.empty())
fout << std::string(maxCheckerSize + 4 - checker.size(), ' ') << "require:" + req;
fout << 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 << std::endl << std::endl;
fout << title << std::endl;
fout << std::string(title.size(), '-') << std::endl;
if (!cppcheckPremium) {
fout << "Not available, Cppcheck Premium is not used" << std::endl;
return;
}
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 (req == "warning")
active &= settings.severity.isEnabled(Severity::warning);
else if (req == "style")
active &= settings.severity.isEnabled(Severity::style);
else if (!req.empty())
active = false; // FIXME: handle req
fout << (active ? "Yes " : "No ") << checker;
if (!req.empty())
req = "premium," + req;
else
req = "premium";
if (!active)
fout << std::string(maxCheckerSize + 4 - checker.size(), ' ') << "require:" + req;
fout << 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 C++", 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 << std::endl << std::endl;
fout << "Misra C" << std::endl;
fout << "-------" << std::endl;
fout << "Misra is not enabled" << std::endl;
} else {
fout << std::endl << std::endl;
fout << "Misra C " << misra << std::endl;
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 std::string severity = getMisraRuleSeverity(rule);
const bool active = isMisraRuleActive(rule, info.amendment, severity, mSettings);
const bool inconclusive = isMisraRuleInconclusive(rule);
fout << (active ? "Yes " : "No ") << 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 (severity != "error")
reqs += "," + severity;
if (inconclusive)
reqs += ",inconclusive";
if (!active && !reqs.empty())
extra += " require:" + reqs.substr(1);
if (!extra.empty())
fout << std::string(7 - rule.size(), ' ') << extra;
fout << '\n';
}
}
reportSection("Misra C++ 2008", mSettings, mActiveCheckers, checkers::premiumCheckers, "Misra C++: ");
return fout.str();
}