Skip to content

Commit 276f017

Browse files
authored
Partial fix for #11897 (Safety: show what checks are enabled/disabled) (#5372)
Example output on stdout: ``` $ ./cppcheck m1.cpp Checking m1.cpp ... Active checkers: 59/177 ```
1 parent 4d18f3e commit 276f017

42 files changed

Lines changed: 1434 additions & 23 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -638,7 +638,7 @@ $(libcppdir)/vfvalue.o: lib/vfvalue.cpp lib/config.h lib/errortypes.h lib/mathli
638638
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
639639
$(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ cli/cmdlineparser.cpp
640640

641-
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/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
641+
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
642642
$(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ cli/cppcheckexecutor.cpp
643643

644644
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

cli/cmdlineparser.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,9 @@ bool CmdLineParser::parseFromArgs(int argc, const char* const argv[])
253253
mSettings.checkLibrary = true;
254254
}
255255

256+
else if (std::strncmp(argv[i], "--checkers-report=", 18) == 0)
257+
mSettings.checkersReportFilename = argv[i] + 18;
258+
256259
else if (std::strncmp(argv[i], "--checks-max-time=", 18) == 0) {
257260
if (!parseNumberArg(argv[i], 18, mSettings.checksMaxTime, true))
258261
return false;
@@ -1115,6 +1118,8 @@ void CmdLineParser::printHelp()
11151118
" The default choice is 'normal'.\n"
11161119
" --check-library Show information messages when library files have\n"
11171120
" incomplete info.\n"
1121+
" --checkers-report=<file>\n"
1122+
" Write a report of all the active checkers to the given file.\n"
11181123
" --clang=<path> Experimental: Use Clang parser instead of the builtin Cppcheck\n"
11191124
" parser. Takes the executable as optional parameter and\n"
11201125
" defaults to `clang`. Cppcheck will run the given Clang\n"

cli/cppcheckexecutor.cpp

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "cppcheckexecutor.h"
2020

2121
#include "analyzerinfo.h"
22+
#include "checkers.h"
2223
#include "cmdlineparser.h"
2324
#include "color.h"
2425
#include "config.h"
@@ -296,6 +297,9 @@ int CppCheckExecutor::check_internal(CppCheck& cppcheck)
296297
AnalyzerInformation::writeFilesTxt(settings.buildDir, fileNames, settings.userDefines, settings.project.fileSettings);
297298
}
298299

300+
if (!settings.checkersReportFilename.empty())
301+
std::remove(settings.checkersReportFilename.c_str());
302+
299303
unsigned int returnValue = 0;
300304
if (settings.useSingleJob()) {
301305
// Single process
@@ -326,11 +330,186 @@ int CppCheckExecutor::check_internal(CppCheck& cppcheck)
326330
reportErr(ErrorMessage::getXMLFooter());
327331
}
328332

333+
writeCheckersReport(settings);
334+
329335
if (returnValue)
330336
return settings.exitCode;
331337
return 0;
332338
}
333339

340+
static bool isCppcheckPremium(const Settings& settings) {
341+
return (settings.cppcheckCfgProductName.compare(0, 16, "Cppcheck Premium") == 0);
342+
}
343+
344+
static std::string getMisraRuleSeverity(const std::string& rule) {
345+
if (checkers::misraRuleSeverity.count(rule) > 0)
346+
return checkers::misraRuleSeverity.at(rule);
347+
return "style";
348+
}
349+
350+
static bool isMisraRuleInconclusive(const std::string& rule) {
351+
return rule == "8.3";
352+
}
353+
354+
static bool isMisraRuleActive(const std::string& rule, int amendment, const std::string& severity, const Settings& settings) {
355+
if (!isCppcheckPremium(settings) && amendment >= 3)
356+
return false;
357+
const bool inconclusive = isMisraRuleInconclusive(rule);
358+
if (inconclusive && !settings.certainty.isEnabled(Certainty::inconclusive))
359+
return false;
360+
if (severity == "warning")
361+
return settings.severity.isEnabled(Severity::warning);
362+
if (severity == "style")
363+
return settings.severity.isEnabled(Severity::style);
364+
return true; // error severity
365+
}
366+
367+
void CppCheckExecutor::writeCheckersReport(const Settings& settings) const
368+
{
369+
if (!settings.quiet) {
370+
int activeCheckers = 0;
371+
int totalCheckers = 0;
372+
for (const auto& checkReq: checkers::allCheckers) {
373+
if (mActiveCheckers.count(checkReq.first) > 0)
374+
++activeCheckers;
375+
++totalCheckers;
376+
}
377+
if (isCppcheckPremium(settings)) {
378+
for (const auto& checkReq: checkers::premiumCheckers) {
379+
if (mActiveCheckers.count(checkReq.first) > 0)
380+
++activeCheckers;
381+
++totalCheckers;
382+
}
383+
}
384+
if (mSettings->premiumArgs.find("misra-c-") != std::string::npos || mSettings->addons.count("misra")) {
385+
for (const checkers::MisraInfo& info: checkers::misraC2012Rules) {
386+
const std::string rule = std::to_string(info.a) + "." + std::to_string(info.b);
387+
const std::string severity = getMisraRuleSeverity(rule);
388+
const bool active = isMisraRuleActive(rule, info.amendment, severity, settings);
389+
if (active)
390+
++activeCheckers;
391+
++totalCheckers;
392+
}
393+
}
394+
395+
const std::string extra = settings.verbose ? " (use --checkers-report=<filename> to see details)" : "";
396+
if (mCriticalErrors.empty())
397+
std::cout << "Active checkers: " << activeCheckers << "/" << totalCheckers << extra << std::endl;
398+
else
399+
std::cout << "Active checkers: There was critical errors" << extra << std::endl;
400+
}
401+
402+
if (settings.checkersReportFilename.empty())
403+
return;
404+
405+
std::ofstream fout(settings.checkersReportFilename);
406+
if (!fout.is_open())
407+
return;
408+
409+
fout << "Critical errors" << std::endl;
410+
fout << "---------------" << std::endl;
411+
if (!mCriticalErrors.empty()) {
412+
fout << "There was critical errors (" << mCriticalErrors << ")" << std::endl;
413+
fout << "All checking is skipped for a file with such error" << std::endl;
414+
} else {
415+
fout << "No critical errors, all files were checked." << std::endl;
416+
fout << "Important: Analysis is still not guaranteed to be 'complete' it is possible there are false negatives." << std::endl;
417+
}
418+
419+
fout << std::endl << std::endl;
420+
fout << "Open source checkers" << std::endl;
421+
fout << "--------------------" << std::endl;
422+
423+
int maxCheckerSize = 0;
424+
for (const auto& checkReq: checkers::allCheckers) {
425+
const std::string& checker = checkReq.first;
426+
if (checker.size() > maxCheckerSize)
427+
maxCheckerSize = checker.size();
428+
}
429+
for (const auto& checkReq: checkers::allCheckers) {
430+
const std::string& checker = checkReq.first;
431+
const bool active = mActiveCheckers.count(checkReq.first) > 0;
432+
const std::string& req = checkReq.second;
433+
fout << (active ? "Yes " : "No ") << checker;
434+
if (!active && !req.empty())
435+
fout << std::string(maxCheckerSize + 4 - checker.size(), ' ') << "require:" + req;
436+
fout << std::endl;
437+
}
438+
439+
const bool cppcheckPremium = isCppcheckPremium(settings);
440+
441+
if (cppcheckPremium) {
442+
fout << std::endl << std::endl;
443+
fout << "Premium checkers" << std::endl;
444+
fout << "----------------" << std::endl;
445+
446+
maxCheckerSize = 0;
447+
for (const auto& checkReq: checkers::premiumCheckers) {
448+
const std::string& checker = checkReq.first;
449+
if (checker.size() > maxCheckerSize)
450+
maxCheckerSize = checker.size();
451+
}
452+
for (const auto& checkReq: checkers::premiumCheckers) {
453+
const std::string& checker = checkReq.first;
454+
std::string req = checkReq.second;
455+
bool active = cppcheckPremium;
456+
if (req == "warning")
457+
active &= mSettings->severity.isEnabled(Severity::warning);
458+
else if (req == "style")
459+
active &= mSettings->severity.isEnabled(Severity::style);
460+
fout << (active ? "Yes " : "No ") << checker;
461+
if (!req.empty())
462+
req = "premium," + req;
463+
else
464+
req = "premium";
465+
if (!active)
466+
fout << std::string(maxCheckerSize + 4 - checker.size(), ' ') << "require:" + req;
467+
fout << std::endl;
468+
}
469+
}
470+
471+
int misra = 0;
472+
if (mSettings->premiumArgs.find("misra-c-2012") != std::string::npos)
473+
misra = 2012;
474+
else if (mSettings->premiumArgs.find("misra-c-2023") != std::string::npos)
475+
misra = 2023;
476+
else if (mSettings->addons.count("misra"))
477+
misra = 2012;
478+
479+
if (misra == 0) {
480+
fout << std::endl << std::endl;
481+
fout << "Misra C" << std::endl;
482+
fout << "-------" << std::endl;
483+
fout << "Misra is not enabled" << std::endl;
484+
} else {
485+
fout << std::endl << std::endl;
486+
fout << "Misra C " << misra << std::endl;
487+
fout << "------------" << std::endl;
488+
for (const checkers::MisraInfo& info: checkers::misraC2012Rules) {
489+
const std::string rule = std::to_string(info.a) + "." + std::to_string(info.b);
490+
const std::string severity = getMisraRuleSeverity(rule);
491+
const bool active = isMisraRuleActive(rule, info.amendment, severity, settings);
492+
const bool inconclusive = isMisraRuleInconclusive(rule);
493+
fout << (active ? "Yes " : "No ") << rule;
494+
std::string extra;
495+
if (misra == 2012 && info.amendment >= 1)
496+
extra = " amendment:" + std::to_string(info.amendment);
497+
std::string reqs;
498+
if (info.amendment >= 3)
499+
reqs += ",premium";
500+
if (severity != "error")
501+
reqs += "," + severity;
502+
if (inconclusive)
503+
reqs += ",inconclusive";
504+
if (!active && !reqs.empty())
505+
extra += " require:" + reqs.substr(1);
506+
if (!extra.empty())
507+
fout << std::string(7 - rule.size(), ' ') << extra;
508+
fout << '\n';
509+
}
510+
}
511+
}
512+
334513
bool CppCheckExecutor::loadLibraries(Settings& settings)
335514
{
336515
if (!tryLoadLibrary(settings.library, settings.exename, "std.cfg")) {
@@ -428,10 +607,22 @@ void CppCheckExecutor::reportErr(const ErrorMessage &msg)
428607
{
429608
assert(mSettings != nullptr);
430609

610+
if (msg.severity == Severity::none && msg.id == "logChecker") {
611+
const std::string& checker = msg.shortMessage();
612+
mActiveCheckers.emplace(checker);
613+
return;
614+
}
615+
431616
// Alert only about unique errors
432617
if (!mShownErrors.insert(msg.toString(mSettings->verbose)).second)
433618
return;
434619

620+
if (ErrorLogger::isCriticalErrorId(msg.id) && mCriticalErrors.find(msg.id) == std::string::npos) {
621+
if (!mCriticalErrors.empty())
622+
mCriticalErrors += ", ";
623+
mCriticalErrors += msg.id;
624+
}
625+
435626
if (mSettings->xml)
436627
reportErr(msg.toXML());
437628
else

cli/cppcheckexecutor.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,11 @@ class CppCheckExecutor : public ErrorLogger {
153153
*/
154154
bool loadLibraries(Settings& settings);
155155

156+
/**
157+
* @brief Write the checkers report
158+
*/
159+
void writeCheckersReport(const Settings& settings) const;
160+
156161
/**
157162
* Pointer to current settings; set while check() is running for reportError().
158163
*/
@@ -182,6 +187,16 @@ class CppCheckExecutor : public ErrorLogger {
182187
* Error output
183188
*/
184189
std::ofstream* mErrorOutput{};
190+
191+
/**
192+
* Checkers that has been executed
193+
*/
194+
std::set<std::string> mActiveCheckers;
195+
196+
/**
197+
* True if there are critical errors
198+
*/
199+
std::string mCriticalErrors;
185200
};
186201

187202
#endif // CPPCHECKEXECUTOR_H

gui/resultsview.cpp

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -527,18 +527,7 @@ void ResultsView::stopAnalysis()
527527

528528
void ResultsView::handleCriticalError(const ErrorItem &item)
529529
{
530-
const QSet<QString> criticalErrors{
531-
"cppcheckError",
532-
"cppcheckLimit",
533-
"internalAstError",
534-
"instantiationError",
535-
"internalError",
536-
"preprocessorErrorDirective",
537-
"syntaxError",
538-
"unknownMacro"
539-
};
540-
541-
if (criticalErrors.contains(item.errorId)) {
530+
if (ErrorLogger::isCriticalErrorId(item.errorId.toStdString())) {
542531
QString msg = tr("There was a critical error with id '%1'").arg(item.errorId);
543532
if (!item.file0.isEmpty())
544533
msg += ", " + tr("when checking %1").arg(item.file0);

lib/check.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,3 +125,9 @@ ErrorPath Check::getErrorPath(const Token* errtok, const ValueFlow::Value* value
125125
}
126126
return errorPath;
127127
}
128+
129+
void Check::logChecker(const char id[])
130+
{
131+
reportError(nullptr, Severity::none, "logChecker", id);
132+
}
133+

lib/check.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,9 @@ class CPPCHECKLIB Check {
155155

156156
void reportError(const ErrorPath &errorPath, Severity::SeverityType severity, const char id[], const std::string &msg, const CWE &cwe, Certainty certainty);
157157

158+
/** log checker */
159+
void logChecker(const char id[]);
160+
158161
ErrorPath getErrorPath(const Token* errtok, const ValueFlow::Value* value, std::string bug) const;
159162

160163
/**

lib/check64bit.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ void Check64BitPortability::pointerassignment()
4646
if (!mSettings->severity.isEnabled(Severity::portability))
4747
return;
4848

49+
logChecker("Check64BitPortability::pointerassignment"); // portability
50+
4951
const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
5052

5153
// Check return values

lib/checkassert.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ void CheckAssert::assertWithSideEffects()
4444
if (!mSettings->severity.isEnabled(Severity::warning))
4545
return;
4646

47+
logChecker("CheckAssert::assertWithSideEffects"); // warning
48+
4749
for (const Token* tok = mTokenizer->list.front(); tok; tok = tok->next()) {
4850
if (!Token::simpleMatch(tok, "assert ("))
4951
continue;

lib/checkautovariables.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,8 @@ void CheckAutoVariables::assignFunctionArg()
213213
if (!printStyle && !printWarning)
214214
return;
215215

216+
logChecker("CheckAutoVariables::assignFunctionArg"); // style,warning
217+
216218
const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
217219
for (const Scope * scope : symbolDatabase->functionScopes) {
218220
for (const Token *tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) {
@@ -261,6 +263,8 @@ static bool hasOverloadedAssignment(const Token* tok, bool c, bool& inconclusive
261263

262264
void CheckAutoVariables::autoVariables()
263265
{
266+
logChecker("CheckAutoVariables::autoVariables");
267+
264268
const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive);
265269
const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
266270
for (const Scope * scope : symbolDatabase->functionScopes) {
@@ -674,6 +678,7 @@ void CheckAutoVariables::checkVarLifetimeScope(const Token * start, const Token
674678

675679
void CheckAutoVariables::checkVarLifetime()
676680
{
681+
logChecker("CheckAutoVariables::checkVarLifetime");
677682
const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
678683
for (const Scope * scope : symbolDatabase->functionScopes) {
679684
if (!scope->function)

0 commit comments

Comments
 (0)