Fix #11897 (Safety: show what checks are enabled/disabled) (#5378)

This primarily adds the corresponding report in the GUI that we have in
the command line already
This commit is contained in:
Daniel Marjamäki 2023-08-31 18:28:47 +02:00 committed by GitHub
parent 22547beaaf
commit 44c149e51b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 562 additions and 304 deletions

View File

@ -200,6 +200,7 @@ LIBOBJ = $(libcppdir)/analyzerinfo.o \
$(libcppdir)/checkbufferoverrun.o \
$(libcppdir)/checkclass.o \
$(libcppdir)/checkcondition.o \
$(libcppdir)/checkersreport.o \
$(libcppdir)/checkexceptionsafety.o \
$(libcppdir)/checkfunctions.o \
$(libcppdir)/checkinternal.o \
@ -488,6 +489,9 @@ $(libcppdir)/checkclass.o: lib/checkclass.cpp externals/tinyxml2/tinyxml2.h lib/
$(libcppdir)/checkcondition.o: lib/checkcondition.cpp lib/astutils.h lib/check.h lib/checkcondition.h lib/checkother.h lib/config.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkcondition.cpp
$(libcppdir)/checkersreport.o: lib/checkersreport.cpp lib/checkers.h lib/checkersreport.h lib/config.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/utils.h
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkersreport.cpp
$(libcppdir)/checkexceptionsafety.o: lib/checkexceptionsafety.cpp lib/check.h lib/checkexceptionsafety.h lib/config.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkexceptionsafety.cpp
@ -638,7 +642,7 @@ $(libcppdir)/vfvalue.o: lib/vfvalue.cpp lib/config.h lib/errortypes.h lib/mathli
cli/cmdlineparser.o: cli/cmdlineparser.cpp cli/cmdlineparser.h cli/cppcheckexecutor.h cli/filelister.h externals/tinyxml2/tinyxml2.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.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/cmdlineparser.cpp
cli/cppcheckexecutor.o: cli/cppcheckexecutor.cpp cli/cmdlineparser.h cli/cppcheckexecutor.h cli/cppcheckexecutorseh.h cli/cppcheckexecutorsig.h cli/executor.h cli/filelister.h cli/processexecutor.h cli/singleexecutor.h cli/threadexecutor.h lib/analyzerinfo.h lib/check.h lib/checkers.h lib/checkunusedfunctions.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/pathmatch.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/utils.h
cli/cppcheckexecutor.o: cli/cppcheckexecutor.cpp cli/cmdlineparser.h cli/cppcheckexecutor.h cli/cppcheckexecutorseh.h cli/cppcheckexecutorsig.h cli/executor.h cli/filelister.h cli/processexecutor.h cli/singleexecutor.h cli/threadexecutor.h lib/analyzerinfo.h lib/check.h lib/checkers.h lib/checkersreport.h lib/checkunusedfunctions.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/pathmatch.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/utils.h
$(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ cli/cppcheckexecutor.cpp
cli/cppcheckexecutorseh.o: cli/cppcheckexecutorseh.cpp cli/cppcheckexecutor.h cli/cppcheckexecutorseh.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/utils.h

View File

@ -14,9 +14,6 @@
</Filter>
</ItemGroup>
<ItemGroup Label="HeaderFiles">
<ClInclude Include="..\lib\config.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="cmdlineparser.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -44,6 +41,9 @@
<ClInclude Include="stacktrace.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="singleexecutor.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup Label="SourceFiles">
<ClCompile Include="main.cpp">
@ -76,6 +76,9 @@
<ClCompile Include="stacktrace.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="singleexecutor.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="version.rc">

View File

@ -20,6 +20,7 @@
#include "analyzerinfo.h"
#include "checkers.h"
#include "checkersreport.h"
#include "cmdlineparser.h"
#include "color.h"
#include "config.h"
@ -337,60 +338,13 @@ int CppCheckExecutor::check_internal(CppCheck& cppcheck)
return 0;
}
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
}
void CppCheckExecutor::writeCheckersReport(const Settings& settings) const
{
CheckersReport checkersReport(settings, mActiveCheckers);
if (!settings.quiet) {
int activeCheckers = 0;
int totalCheckers = 0;
for (const auto& checkReq: checkers::allCheckers) {
if (mActiveCheckers.count(checkReq.first) > 0)
++activeCheckers;
++totalCheckers;
}
if (isCppcheckPremium(settings)) {
for (const auto& checkReq: checkers::premiumCheckers) {
if (mActiveCheckers.count(checkReq.first) > 0)
++activeCheckers;
++totalCheckers;
}
}
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, settings);
if (active)
++activeCheckers;
++totalCheckers;
}
}
const int activeCheckers = checkersReport.getActiveCheckersCount();
const int totalCheckers = checkersReport.getAllCheckersCount();
const std::string extra = settings.verbose ? " (use --checkers-report=<filename> to see details)" : "";
if (mCriticalErrors.empty())
@ -403,111 +357,9 @@ void CppCheckExecutor::writeCheckersReport(const Settings& settings) const
return;
std::ofstream fout(settings.checkersReportFilename);
if (!fout.is_open())
return;
if (fout.is_open())
fout << checkersReport.getReport(mCriticalErrors);
fout << "Critical errors" << std::endl;
fout << "---------------" << std::endl;
if (!mCriticalErrors.empty()) {
fout << "There was critical errors (" << mCriticalErrors << ")" << 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(settings);
if (cppcheckPremium) {
fout << std::endl << std::endl;
fout << "Premium checkers" << std::endl;
fout << "----------------" << std::endl;
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;
if (req == "warning")
active &= mSettings->severity.isEnabled(Severity::warning);
else if (req == "style")
active &= mSettings->severity.isEnabled(Severity::style);
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, settings);
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';
}
}
}
bool CppCheckExecutor::loadLibraries(Settings& settings)
@ -607,7 +459,7 @@ void CppCheckExecutor::reportErr(const ErrorMessage &msg)
{
assert(mSettings != nullptr);
if (msg.severity == Severity::none && msg.id == "logChecker") {
if (msg.severity == Severity::none && (msg.id == "logChecker" || endsWith(msg.id, "-logChecker"))) {
const std::string& checker = msg.shortMessage();
mActiveCheckers.emplace(checker);
return;

View File

@ -65,6 +65,11 @@ void CheckStatistics::addItem(const QString &tool, ShowTypes::ShowType type)
}
}
void CheckStatistics::addChecker(const QString &checker)
{
mActiveCheckers.insert(checker.toStdString());
}
void CheckStatistics::clear()
{
mStyle.clear();
@ -73,6 +78,8 @@ void CheckStatistics::clear()
mPortability.clear();
mInformation.clear();
mError.clear();
mActiveCheckers.clear();
mCheckersReport.clear();
}
unsigned CheckStatistics::getCount(const QString &tool, ShowTypes::ShowType type) const

View File

@ -23,9 +23,13 @@
#include <QMap>
#include <QObject>
#include <QSet>
#include <QString>
#include <QStringList>
#include <set>
#include <string>
/// @addtogroup GUI
/// @{
@ -44,6 +48,11 @@ public:
*/
void addItem(const QString &tool, ShowTypes::ShowType type);
/**
* @brief Add checker to statistics
*/
void addChecker(const QString& checker);
/**
* @brief Clear the statistics.
*
@ -59,9 +68,24 @@ public:
*/
unsigned getCount(const QString &tool, ShowTypes::ShowType type) const;
std::set<std::string> getActiveCheckers() const {
return mActiveCheckers;
}
int getNumberOfActiveCheckers() const {
return mActiveCheckers.size();
}
/** Get tools with results */
QStringList getTools() const;
void setCheckersReport(QString report) {
mCheckersReport = std::move(report);
}
QString getCheckersReport() const {
return mCheckersReport;
}
private:
QMap<QString, unsigned> mStyle;
QMap<QString, unsigned> mWarning;
@ -69,6 +93,8 @@ private:
QMap<QString, unsigned> mPortability;
QMap<QString, unsigned> mInformation;
QMap<QString, unsigned> mError;
std::set<std::string> mActiveCheckers;
QString mCheckersReport;
};
/// @}

View File

@ -540,6 +540,7 @@ void MainWindow::doAnalyzeProject(ImportProject p, const bool checkLibrary, cons
}
mThread->setProject(p);
mThread->check(checkSettings);
mUI->mResults->setCheckSettings(checkSettings);
}
void MainWindow::doAnalyzeFiles(const QStringList &files, const bool checkLibrary, const bool checkConfiguration)
@ -603,6 +604,7 @@ void MainWindow::doAnalyzeFiles(const QStringList &files, const bool checkLibrar
mThread->setCheckFiles(true);
mThread->check(checkSettings);
mUI->mResults->setCheckSettings(checkSettings);
}
void MainWindow::analyzeCode(const QString& code, const QString& filename)
@ -1208,7 +1210,9 @@ void MainWindow::reAnalyzeSelected(const QStringList& files)
// considered in "Modified Files Check" performed after "Selected Files Check"
// TODO: Should we store per file CheckStartTime?
QDateTime saveCheckStartTime = mThread->getCheckStartTime();
mThread->check(getCppcheckSettings());
const Settings& checkSettings = getCppcheckSettings();
mThread->check(checkSettings);
mUI->mResults->setCheckSettings(checkSettings);
mThread->setCheckStartTime(saveCheckStartTime);
}
@ -1232,7 +1236,9 @@ void MainWindow::reAnalyze(bool all)
qDebug() << "Rechecking project file" << mProjectFile->getFilename();
mThread->setCheckFiles(all);
mThread->check(getCppcheckSettings());
const Settings& checkSettings = getCppcheckSettings();
mThread->check(checkSettings);
mUI->mResults->setCheckSettings(checkSettings);
}
void MainWindow::clearResults()

View File

@ -193,7 +193,7 @@ public slots:
/** @brief Slot for showing the library editor */
void showLibraryEditor();
protected slots:
private slots:
/** @brief Slot for checkthread's done signal */
void analysisDone();

View File

@ -19,6 +19,7 @@
#include "resultsview.h"
#include "checkstatistics.h"
#include "checkersreport.h"
#include "codeeditor.h"
#include "codeeditorstyle.h"
#include "common.h"
@ -105,6 +106,7 @@ void ResultsView::initialize(QSettings *settings, ApplicationList *list, ThreadH
ResultsView::~ResultsView()
{
delete mUI;
delete mCheckSettings;
}
void ResultsView::clear(bool results)
@ -116,6 +118,8 @@ void ResultsView::clear(bool results)
mUI->mDetails->setText(QString());
mStatistics->clear();
delete mCheckSettings;
mCheckSettings = nullptr;
//Clear the progressbar
mUI->mProgress->setMaximum(PROGRESS_MAX);
@ -150,6 +154,11 @@ void ResultsView::progress(int value, const QString& description)
void ResultsView::error(const ErrorItem &item)
{
if (item.severity == Severity::none && (item.errorId == "logChecker" || item.errorId.endsWith("-logChecker"))) {
mStatistics->addChecker(item.message);
return;
}
handleCriticalError(item);
if (mUI->mTree->addErrorItem(item)) {
@ -282,6 +291,13 @@ QString ResultsView::getCheckDirectory()
return mUI->mTree->getCheckDirectory();
}
void ResultsView::setCheckSettings(const Settings &settings)
{
delete mCheckSettings;
mCheckSettings = new Settings;
*mCheckSettings = settings;
}
void ResultsView::checkingStarted(int count)
{
mSuccess = true;
@ -296,6 +312,13 @@ void ResultsView::checkingFinished()
mUI->mProgress->setVisible(false);
mUI->mProgress->setFormat("%p%");
{
Settings checkSettings;
const std::set<std::string> activeCheckers = mStatistics->getActiveCheckers();
CheckersReport checkersReport(mCheckSettings ? *mCheckSettings : checkSettings, activeCheckers);
mStatistics->setCheckersReport(QString::fromStdString(checkersReport.getReport(mCriticalErrors.toStdString())));
}
// TODO: Items can be mysteriously hidden when checking is finished, this function
// call should be redundant but it "unhides" the wrongly hidden items.
mUI->mTree->refreshTree();
@ -528,6 +551,11 @@ void ResultsView::stopAnalysis()
void ResultsView::handleCriticalError(const ErrorItem &item)
{
if (ErrorLogger::isCriticalErrorId(item.errorId.toStdString())) {
if (!mCriticalErrors.contains(item.errorId)) {
if (!mCriticalErrors.isEmpty())
mCriticalErrors += ",";
mCriticalErrors += item.errorId;
}
QString msg = tr("There was a critical error with id '%1'").arg(item.errorId);
if (!item.file0.isEmpty())
msg += ", " + tr("when checking %1").arg(item.file0);

View File

@ -29,6 +29,7 @@
#include <QWidget>
class ErrorItem;
class Settings;
class ApplicationList;
class ThreadHandler;
class QModelIndex;
@ -136,6 +137,11 @@ public:
QString getCheckDirectory();
/**
* Set settings used in checking
*/
void setCheckSettings(const Settings& settings);
/**
* @brief Inform the view that checking has started
*
@ -368,11 +374,16 @@ private:
CheckStatistics *mStatistics;
Settings* mCheckSettings = nullptr;
/**
* Set to true when checking finish successfully. Set to false whenever analysis starts.
*/
bool mSuccess = false;
/** Critical error ids */
QString mCriticalErrors;
private slots:
/**
* @brief Custom context menu for Analysis Log

View File

@ -75,6 +75,10 @@ StatsDialog::StatsDialog(QWidget *parent)
{
mUI->setupUi(this);
QFont font("courier");
font.setStyleHint(QFont::Monospace);
mUI->mCheckersReport->setFont(font);
setWindowFlags(Qt::Window);
connect(mUI->mCopyToClipboard, &QPushButton::pressed, this, &StatsDialog::copyToClipboard);
@ -366,12 +370,14 @@ void StatsDialog::copyToClipboard()
void StatsDialog::setStatistics(const CheckStatistics *stats)
{
mStatistics = stats;
mUI->mLblErrors->setText(QString("%1").arg(stats->getCount(CPPCHECK,ShowTypes::ShowErrors)));
mUI->mLblWarnings->setText(QString("%1").arg(stats->getCount(CPPCHECK,ShowTypes::ShowWarnings)));
mUI->mLblStyle->setText(QString("%1").arg(stats->getCount(CPPCHECK,ShowTypes::ShowStyle)));
mUI->mLblPortability->setText(QString("%1").arg(stats->getCount(CPPCHECK,ShowTypes::ShowPortability)));
mUI->mLblPerformance->setText(QString("%1").arg(stats->getCount(CPPCHECK,ShowTypes::ShowPerformance)));
mUI->mLblInformation->setText(QString("%1").arg(stats->getCount(CPPCHECK,ShowTypes::ShowInformation)));
mUI->mLblErrors->setText(QString::number(stats->getCount(CPPCHECK,ShowTypes::ShowErrors)));
mUI->mLblWarnings->setText(QString::number(stats->getCount(CPPCHECK,ShowTypes::ShowWarnings)));
mUI->mLblStyle->setText(QString::number(stats->getCount(CPPCHECK,ShowTypes::ShowStyle)));
mUI->mLblPortability->setText(QString::number(stats->getCount(CPPCHECK,ShowTypes::ShowPortability)));
mUI->mLblPerformance->setText(QString::number(stats->getCount(CPPCHECK,ShowTypes::ShowPerformance)));
mUI->mLblInformation->setText(QString::number(stats->getCount(CPPCHECK,ShowTypes::ShowInformation)));
mUI->mLblActiveCheckers->setText(QString::number(stats->getNumberOfActiveCheckers()));
mUI->mCheckersReport->setPlainText(stats->getCheckersReport());
}
#ifdef QT_CHARTS_LIB

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>502</width>
<height>274</height>
<width>500</width>
<height>414</height>
</rect>
</property>
<property name="windowTitle">
@ -247,114 +247,150 @@
<attribute name="title">
<string>Statistics</string>
</attribute>
<layout class="QFormLayout" name="formLayout">
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Errors:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="mLblErrors">
<property name="text">
<string notr="true">TextLabel</string>
</property>
</widget>
</item>
</layout>
<widget class="QLabel" name="label">
<property name="text">
<string>Errors:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="mLblErrors">
<property name="text">
<string notr="true">TextLabel</string>
</property>
</widget>
</item>
<item row="1" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Warnings:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="mLblWarnings">
<property name="text">
<string notr="true">TextLabel</string>
</property>
</widget>
</item>
</layout>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Warnings:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="mLblWarnings">
<property name="text">
<string notr="true">TextLabel</string>
</property>
</widget>
</item>
<item row="2" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Stylistic warnings:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="mLblStyle">
<property name="text">
<string notr="true">TextLabel</string>
</property>
</widget>
</item>
</layout>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Stylistic warnings:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="mLblStyle">
<property name="text">
<string notr="true">TextLabel</string>
</property>
</widget>
</item>
<item row="3" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<widget class="QLabel" name="label_6">
<property name="text">
<string>Portability warnings:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="mLblPortability">
<property name="text">
<string notr="true">TextLabel</string>
</property>
</widget>
</item>
</layout>
<widget class="QLabel" name="label_6">
<property name="text">
<string>Portability warnings:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="mLblPortability">
<property name="text">
<string notr="true">TextLabel</string>
</property>
</widget>
</item>
<item row="4" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Performance issues:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="mLblPerformance">
<property name="text">
<string notr="true">TextLabel</string>
</property>
</widget>
</item>
</layout>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Performance issues:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="mLblPerformance">
<property name="text">
<string notr="true">TextLabel</string>
</property>
</widget>
</item>
<item row="5" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>Information messages:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="mLblInformation">
<property name="text">
<string notr="true">TextLabel</string>
</property>
</widget>
</item>
</layout>
<widget class="QLabel" name="label_5">
<property name="text">
<string>Information messages:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="mLblInformation">
<property name="text">
<string notr="true">TextLabel</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Active checkers:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QLabel" name="mLblActiveCheckers">
<property name="text">
<string notr="true">TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="mCheckersTab">
<attribute name="title">
<string>Checkers</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QPlainTextEdit" name="mCheckersReport">
<property name="enabled">
<bool>true</bool>
</property>
<property name="font">
<font>
<family>Courier 10 Pitch</family>
</font>
</property>
<property name="undoRedoEnabled">
<bool>true</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>

View File

@ -206,22 +206,21 @@ namespace checkers {
static std::map<std::string, std::string> premiumCheckers{
{"CheckBufferOverrun::addressOfPointerArithmetic","warning"},
{"CheckBufferOverrun::negativeBufferSizeCheckedNonZero","warning"},
{"CheckBufferOverrun::negativeBufferSizeCheckedNonZero","warning"},
{"CheckHang::infiniteLoop",""},
{"CheckHang::infiniteLoopContinue",""},
{"CheckOther::arrayPointerComparison","style"},
{"CheckOther::knownResult","style"},
{"CheckOther::lossOfPrecision","style"},
{"CheckOther::pointerCast","style"},
{"CheckOther::reassignInLoop","style"},
{"CheckOther::unreachableCode","style"},
{"CheckStrictAlias::strictAliasCondition","warning"},
{"CheckUninitVar::uninitvar",""},
{"CheckUninitVar::uninitmember",""},
{"CheckUnusedVar::unreadVariable","style"},
{"CheckUnusedVar::unusedPrivateMember","style"},
{"PremiumCheckBufferOverrun::addressOfPointerArithmetic","warning"},
{"PremiumCheckBufferOverrun::negativeBufferSizeCheckedNonZero","warning"},
{"PremiumCheckBufferOverrun::negativeBufferSizeCheckedNonZero","warning"},
{"PremiumCheckHang::infiniteLoop",""},
{"PremiumCheckHang::infiniteLoopContinue",""},
{"PremiumCheckOther::arrayPointerComparison","style"},
{"PremiumCheckOther::knownResult","style"},
{"PremiumCheckOther::lossOfPrecision","style"},
{"PremiumCheckOther::pointerCast","style"},
{"PremiumCheckOther::reassignInLoop","style"},
{"PremiumCheckOther::unreachableCode","style"},
{"PremiumCheckUninitVar::uninitvar",""},
{"PremiumCheckUninitVar::uninitmember",""},
{"PremiumCheckUnusedVar::unreadVariable","style"},
{"PremiumCheckUnusedVar::unusedPrivateMember","style"},
};

207
lib/checkersreport.cpp Normal file
View File

@ -0,0 +1,207 @@
/*
* 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();
}

44
lib/checkersreport.h Normal file
View File

@ -0,0 +1,44 @@
/*
* 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/>.
*/
#pragma once
#include "settings.h"
#include <set>
#include <string>
class CPPCHECKLIB CheckersReport {
public:
CheckersReport(const Settings& settings, const std::set<std::string>& activeCheckers);
int getActiveCheckersCount();
int getAllCheckersCount();
std::string getReport(const std::string& criticalErrors) const;
private:
const Settings& mSettings;
const std::set<std::string>& mActiveCheckers;
void countCheckers();
int mActiveCheckersCount = 0;
int mAllCheckersCount = 0;
};

View File

@ -1492,9 +1492,11 @@ void CppCheck::executeAddons(const std::vector<std::string>& files)
errmsg.setmsg(text);
const std::string severity = obj["severity"].get<std::string>();
errmsg.severity = Severity::fromString(severity);
if (errmsg.severity == Severity::SeverityType::none)
continue;
if (!mSettings.severity.isEnabled(errmsg.severity))
if (errmsg.severity == Severity::SeverityType::none) {
if (!endsWith(errmsg.id, "-logChecker"))
continue;
}
else if (!mSettings.severity.isEnabled(errmsg.severity))
continue;
errmsg.file0 = ((files.size() == 1) ? files[0] : "");
@ -1596,7 +1598,7 @@ void CppCheck::purgedConfigurationMessage(const std::string &file, const std::st
void CppCheck::reportErr(const ErrorMessage &msg)
{
if (msg.severity == Severity::none && msg.id == "logChecker") {
if (msg.severity == Severity::none && (msg.id == "logChecker" || endsWith(msg.id, "-logChecker"))) {
mErrorLogger.reportErr(msg);
return;
}

View File

@ -56,6 +56,7 @@
<ClCompile Include="checkbufferoverrun.cpp" />
<ClCompile Include="checkclass.cpp" />
<ClCompile Include="checkcondition.cpp" />
<ClCompile Include="checkersreport.cpp" />
<ClCompile Include="checkexceptionsafety.cpp" />
<ClCompile Include="checkfunctions.cpp" />
<ClCompile Include="checkinternal.cpp" />
@ -122,6 +123,7 @@
<ClInclude Include="checkbufferoverrun.h" />
<ClInclude Include="checkclass.h" />
<ClInclude Include="checkcondition.h" />
<ClInclude Include="checkersreport.h" />
<ClInclude Include="checkexceptionsafety.h" />
<ClInclude Include="checkfunctions.h" />
<ClInclude Include="checkinternal.h" />

View File

@ -194,6 +194,12 @@
<ClCompile Include="vfvalue.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="checkersreport.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="keywords.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup Label="HeaderFiles">
<ClInclude Include="checkbufferoverrun.h">
@ -406,6 +412,15 @@
<ClInclude Include="vfvalue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="keywords.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="checkersreport.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="json.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="version.rc" />

View File

@ -45,6 +45,7 @@ const std::set<std::string> ErrorLogger::mCriticalErrorIds{
"internalAstError",
"instantiationError",
"internalError",
"premium-internalError",
"preprocessorErrorDirective",
"syntaxError",
"unknownMacro"

View File

@ -16,6 +16,7 @@ HEADERS += $${PWD}/analyzer.h \
$${PWD}/checkbufferoverrun.h \
$${PWD}/checkclass.h \
$${PWD}/checkcondition.h \
$${PWD}/checkersreport.h \
$${PWD}/checkexceptionsafety.h \
$${PWD}/checkfunctions.h \
$${PWD}/checkinternal.h \
@ -85,6 +86,7 @@ SOURCES += $${PWD}/analyzerinfo.cpp \
$${PWD}/checkbufferoverrun.cpp \
$${PWD}/checkclass.cpp \
$${PWD}/checkcondition.cpp \
$${PWD}/checkersreport.cpp \
$${PWD}/checkexceptionsafety.cpp \
$${PWD}/checkfunctions.cpp \
$${PWD}/checkinternal.cpp \

View File

@ -10,8 +10,12 @@ Improved checking:
- constParameter*/constVariable* checks find more instances of pointers/references that can be const, e.g. when calling library functions
GUI:
- Show in statistics which checkers have been activated in latest analysis
- Make it more visible if there has been critical errors that caused checkers to be skipped
Changed interface:
- Write how many checkers was activated after a run
- Added --checkers-report that can be used to generate a report in a file that shows what checkers was activated and disabled
Deprecations:
- The qmake build system has been deprecated and will be removed in a future version.

View File

@ -256,6 +256,9 @@
<ClCompile Include="testsingleexecutor.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="testcheck.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup Label="HeaderFiles">
<ClInclude Include="options.h">

View File

@ -44,22 +44,22 @@ print('static std::map<std::string, std::string> premiumCheckers{')
premium_checkers = """
$ grep logChecker *.cpp | sed 's/.*logChecker/logChecker/'
logChecker("CheckBufferOverrun::addressOfPointerArithmetic"); // warning
logChecker("CheckBufferOverrun::negativeBufferSizeCheckedNonZero"); // warning
logChecker("CheckBufferOverrun::negativeBufferSizeCheckedNonZero"); // warning
logChecker("CheckHang::infiniteLoop");
logChecker("CheckHang::infiniteLoopContinue");
logChecker("CheckOther::arrayPointerComparison"); // style
logChecker("CheckOther::knownResult"); // style
logChecker("CheckOther::lossOfPrecision"); // style
logChecker("CheckOther::pointerCast"); // style
logChecker("CheckOther::reassignInLoop"); // style
logChecker("CheckOther::unreachableCode"); // style
logChecker("CheckStrictAlias::strictAliasCondition"); // warning
logChecker("CheckUninitVar::uninitvar");
logChecker("CheckUninitVar::uninitmember");
logChecker("CheckUnusedVar::unreadVariable"); // style
logChecker("CheckUnusedVar::unusedPrivateMember"); // style
logChecker("PremiumCheckBufferOverrun::addressOfPointerArithmetic"); // warning
logChecker("PremiumCheckBufferOverrun::negativeBufferSizeCheckedNonZero"); // warning
logChecker("PremiumCheckBufferOverrun::negativeBufferSizeCheckedNonZero"); // warning
logChecker("PremiumCheckHang::infiniteLoop");
logChecker("PremiumCheckHang::infiniteLoopContinue");
logChecker("PremiumCheckOther::arrayPointerComparison"); // style
logChecker("PremiumCheckOther::knownResult"); // style
logChecker("PremiumCheckOther::lossOfPrecision"); // style
logChecker("PremiumCheckOther::pointerCast"); // style
logChecker("PremiumCheckOther::reassignInLoop"); // style
logChecker("PremiumCheckOther::unreachableCode"); // style
logChecker("PremiumCheckStrictAlias::strictAliasCondition"); // warning
logChecker("PremiumCheckUninitVar::uninitvar");
logChecker("PremiumCheckUninitVar::uninitmember");
logChecker("PremiumCheckUnusedVar::unreadVariable"); // style
logChecker("PremiumCheckUnusedVar::unusedPrivateMember"); // style
"""
for line in premium_checkers.split('\n'):