208 lines
7.8 KiB
C++
208 lines
7.8 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 <sstream>
|
|
|
|
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);
|
|
|
|
fout << std::endl << std::endl;
|
|
fout << "Premium checkers" << std::endl;
|
|
fout << "----------------" << std::endl;
|
|
if (!cppcheckPremium) {
|
|
fout << "Cppcheck Premium is not used" << std::endl;
|
|
} else {
|
|
maxCheckerSize = 0;
|
|
for (const auto& checkReq: checkers::premiumCheckers) {
|
|
const std::string& checker = checkReq.first;
|
|
if (checker.size() > maxCheckerSize)
|
|
maxCheckerSize = checker.size();
|
|
}
|
|
for (const auto& checkReq: checkers::premiumCheckers) {
|
|
const std::string& checker = checkReq.first;
|
|
std::string req = checkReq.second;
|
|
bool active = cppcheckPremium && mActiveCheckers.count(checkReq.first) > 0;
|
|
if (req == "warning")
|
|
active &= mSettings.severity.isEnabled(Severity::warning);
|
|
else if (req == "style")
|
|
active &= mSettings.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;
|
|
}
|
|
}
|
|
|
|
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';
|
|
}
|
|
}
|
|
|
|
return fout.str();
|
|
}
|