diff --git a/Makefile b/Makefile index 8cc4ead63..d1aa9cc09 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/cli/cli.vcxproj.filters b/cli/cli.vcxproj.filters index e8385a6c8..2b107b060 100644 --- a/cli/cli.vcxproj.filters +++ b/cli/cli.vcxproj.filters @@ -14,9 +14,6 @@ - - Header Files - Header Files @@ -44,6 +41,9 @@ Header Files + + Header Files + @@ -76,6 +76,9 @@ Source Files + + Source Files + diff --git a/cli/cppcheckexecutor.cpp b/cli/cppcheckexecutor.cpp index b16c6ff7f..747d9cb50 100644 --- a/cli/cppcheckexecutor.cpp +++ b/cli/cppcheckexecutor.cpp @@ -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= 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; diff --git a/gui/checkstatistics.cpp b/gui/checkstatistics.cpp index a8f75c9a8..4f812b8ee 100644 --- a/gui/checkstatistics.cpp +++ b/gui/checkstatistics.cpp @@ -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 diff --git a/gui/checkstatistics.h b/gui/checkstatistics.h index f487c39c8..d53be1288 100644 --- a/gui/checkstatistics.h +++ b/gui/checkstatistics.h @@ -23,9 +23,13 @@ #include #include +#include #include #include +#include +#include + /// @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 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 mStyle; QMap mWarning; @@ -69,6 +93,8 @@ private: QMap mPortability; QMap mInformation; QMap mError; + std::set mActiveCheckers; + QString mCheckersReport; }; /// @} diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 5f28d8b98..fb3a3081f 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -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() diff --git a/gui/mainwindow.h b/gui/mainwindow.h index 78782b22b..969e1c28e 100644 --- a/gui/mainwindow.h +++ b/gui/mainwindow.h @@ -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(); diff --git a/gui/resultsview.cpp b/gui/resultsview.cpp index e1cea8833..891d80a49 100644 --- a/gui/resultsview.cpp +++ b/gui/resultsview.cpp @@ -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 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); diff --git a/gui/resultsview.h b/gui/resultsview.h index 70c8ddfa2..d978afab9 100644 --- a/gui/resultsview.h +++ b/gui/resultsview.h @@ -29,6 +29,7 @@ #include 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 diff --git a/gui/statsdialog.cpp b/gui/statsdialog.cpp index 4f5ce9a85..5dfd8a589 100644 --- a/gui/statsdialog.cpp +++ b/gui/statsdialog.cpp @@ -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 diff --git a/gui/statsdialog.ui b/gui/statsdialog.ui index 8147b8e7e..a749c51e6 100644 --- a/gui/statsdialog.ui +++ b/gui/statsdialog.ui @@ -6,8 +6,8 @@ 0 0 - 502 - 274 + 500 + 414 @@ -247,114 +247,150 @@ Statistics - + - - - - - Errors: - - - - - - - TextLabel - - - - + + + Errors: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + TextLabel + + - - - - - Warnings: - - - - - - - TextLabel - - - - + + + Warnings: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + TextLabel + + - - - - - Stylistic warnings: - - - - - - - TextLabel - - - - + + + Stylistic warnings: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + TextLabel + + - - - - - Portability warnings: - - - - - - - TextLabel - - - - + + + Portability warnings: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + TextLabel + + - - - - - Performance issues: - - - - - - - TextLabel - - - - + + + Performance issues: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + TextLabel + + - - - - - Information messages: - - - - - - - TextLabel - - - - + + + Information messages: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + TextLabel + + + + + + + Active checkers: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + TextLabel + + + + + + + + Checkers + + + + + + true + + + + Courier 10 Pitch + + + + true + + + true + + diff --git a/lib/checkers.h b/lib/checkers.h index 5cc61a518..cec27c53f 100644 --- a/lib/checkers.h +++ b/lib/checkers.h @@ -206,22 +206,21 @@ namespace checkers { static std::map 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"}, }; diff --git a/lib/checkersreport.cpp b/lib/checkersreport.cpp new file mode 100644 index 000000000..a90cdf595 --- /dev/null +++ b/lib/checkersreport.cpp @@ -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 . + */ + +#include "checkersreport.h" +#include "checkers.h" +#include + +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& 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(); +} diff --git a/lib/checkersreport.h b/lib/checkersreport.h new file mode 100644 index 000000000..b6acab880 --- /dev/null +++ b/lib/checkersreport.h @@ -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 . + */ + +#pragma once + +#include "settings.h" +#include +#include + +class CPPCHECKLIB CheckersReport { +public: + CheckersReport(const Settings& settings, const std::set& activeCheckers); + + int getActiveCheckersCount(); + int getAllCheckersCount(); + + std::string getReport(const std::string& criticalErrors) const; + +private: + const Settings& mSettings; + const std::set& mActiveCheckers; + + void countCheckers(); + + int mActiveCheckersCount = 0; + int mAllCheckersCount = 0; +}; + + diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index e9fc037dd..481617d34 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -1492,9 +1492,11 @@ void CppCheck::executeAddons(const std::vector& files) errmsg.setmsg(text); const std::string severity = obj["severity"].get(); 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; } diff --git a/lib/cppcheck.vcxproj b/lib/cppcheck.vcxproj index 28e4752d6..db5906ad8 100644 --- a/lib/cppcheck.vcxproj +++ b/lib/cppcheck.vcxproj @@ -56,6 +56,7 @@ + @@ -122,6 +123,7 @@ + diff --git a/lib/cppcheck.vcxproj.filters b/lib/cppcheck.vcxproj.filters index 2bba20684..c3e5d3466 100644 --- a/lib/cppcheck.vcxproj.filters +++ b/lib/cppcheck.vcxproj.filters @@ -194,6 +194,12 @@ Source Files + + Source Files + + + Source Files + @@ -406,6 +412,15 @@ Header Files + + Header Files + + + Header Files + + + Header Files + diff --git a/lib/errorlogger.cpp b/lib/errorlogger.cpp index 049b51887..af322cb02 100644 --- a/lib/errorlogger.cpp +++ b/lib/errorlogger.cpp @@ -45,6 +45,7 @@ const std::set ErrorLogger::mCriticalErrorIds{ "internalAstError", "instantiationError", "internalError", + "premium-internalError", "preprocessorErrorDirective", "syntaxError", "unknownMacro" diff --git a/lib/lib.pri b/lib/lib.pri index 09676f2de..6c31cc91c 100644 --- a/lib/lib.pri +++ b/lib/lib.pri @@ -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 \ diff --git a/releasenotes.txt b/releasenotes.txt index 0e69003df..e302a8481 100644 --- a/releasenotes.txt +++ b/releasenotes.txt @@ -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. diff --git a/test/testrunner.vcxproj.filters b/test/testrunner.vcxproj.filters index db85b4d9e..7aa2676a8 100644 --- a/test/testrunner.vcxproj.filters +++ b/test/testrunner.vcxproj.filters @@ -256,6 +256,9 @@ Source Files + + Source Files + diff --git a/tools/get_checkers.py b/tools/get_checkers.py index b43e457a0..510b2d827 100644 --- a/tools/get_checkers.py +++ b/tools/get_checkers.py @@ -44,22 +44,22 @@ print('static std::map 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'):