diff --git a/Makefile b/Makefile index b19f746feae..ea6be636b9a 100644 --- a/Makefile +++ b/Makefile @@ -685,7 +685,7 @@ cli/processexecutor.o: cli/processexecutor.cpp cli/executor.h cli/processexecuto cli/signalhandler.o: cli/signalhandler.cpp cli/signalhandler.h cli/stacktrace.h lib/config.h $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ cli/signalhandler.cpp -cli/singleexecutor.o: cli/singleexecutor.cpp cli/executor.h cli/singleexecutor.h lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/filesettings.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 +cli/singleexecutor.o: cli/singleexecutor.cpp cli/executor.h cli/singleexecutor.h lib/addoninfo.h lib/check.h lib/config.h lib/cppcheck.h lib/errortypes.h lib/filesettings.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 $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ cli/singleexecutor.cpp cli/stacktrace.o: cli/stacktrace.cpp cli/stacktrace.h lib/config.h lib/utils.h diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index 63edaf3cd6e..ef662707c58 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -25,6 +25,7 @@ #include "clangimport.h" #include "color.h" #include "ctu.h" +#include "errorlogger.h" #include "errortypes.h" #include "filesettings.h" #include "library.h" @@ -52,6 +53,7 @@ #include // IWYU pragma: keep #include #include +#include #include #include #include @@ -86,6 +88,200 @@ static TimerResults s_timerResults; // CWE ids used static const CWE CWE398(398U); // Indicator of Poor Code Quality +class CppCheck::CppCheckLogger : public ErrorLogger +{ +public: + CppCheckLogger(ErrorLogger& errorLogger, const Settings& settings, Suppressions& suppressions, bool useGlobalSuppressions) + : mErrorLogger(errorLogger) + , mSettings(settings) + , mSuppressions(suppressions) + , mUseGlobalSuppressions(useGlobalSuppressions) + {} + + ~CppCheckLogger() override + { + closePlist(); + } + + void setRemarkComments(std::vector remarkComments) + { + mRemarkComments = std::move(remarkComments); + } + + void setLocationMacros(const Token* startTok, const std::vector& files) + { + mLocationMacros.clear(); + for (const Token* tok = startTok; tok; tok = tok->next()) { + if (!tok->getMacroName().empty()) + mLocationMacros[Location(files[tok->fileIndex()], tok->linenr())].emplace(tok->getMacroName()); + } + } + + void resetExitCode() + { + mExitCode = 0; + } + + void clear() + { + mErrorList.clear(); + } + + void openPlist(const std::string& filename, const std::vector& files) + { + mPlistFile.open(filename); + mPlistFile << ErrorLogger::plistHeader(version(), files); + } + + void closePlist() + { + if (mPlistFile.is_open()) { + mPlistFile << ErrorLogger::plistFooter(); + mPlistFile.close(); + } + } + + unsigned int exitcode() const + { + return mExitCode; + } + + void setAnalyzerInfo(AnalyzerInformation* info) + { + mAnalyzerInformation = info; + } + +private: + /** + * @brief Errors and warnings are directed here. + * + * @param msg Errors messages are normally in format + * "[filepath:line number] Message", e.g. + * "[main.cpp:4] Uninitialized member variable" + */ + // TODO: part of this logic is duplicated in Executor::hasToLog() + void reportErr(const ErrorMessage &msg) override + { + if (msg.severity == Severity::internal) { + mErrorLogger.reportErr(msg); + return; + } + + if (!mSettings.library.reportErrors(msg.file0)) + return; + + std::set macroNames; + if (!msg.callStack.empty()) { + const std::string &file = msg.callStack.back().getfile(false); + int lineNumber = msg.callStack.back().line; + const auto it = mLocationMacros.find(Location(file, lineNumber)); + if (it != mLocationMacros.cend()) + macroNames = it->second; + } + + // TODO: only convert if necessary + const auto errorMessage = SuppressionList::ErrorMessage::fromErrorMessage(msg, macroNames); + + if (mSuppressions.nomsg.isSuppressed(errorMessage, mUseGlobalSuppressions)) { + // Safety: Report critical errors to ErrorLogger + if (mSettings.safety && ErrorLogger::isCriticalErrorId(msg.id)) { + mExitCode = 1; + + if (mSuppressions.nomsg.isSuppressedExplicitly(errorMessage, mUseGlobalSuppressions)) { + // Report with internal severity to signal that there is this critical error but + // it is suppressed + ErrorMessage temp(msg); + temp.severity = Severity::internal; + mErrorLogger.reportErr(temp); + } else { + // Report critical error that is not explicitly suppressed + mErrorLogger.reportErr(msg); + } + } + return; + } + + // TODO: there should be no need for the verbose and default messages here + std::string errmsg = msg.toString(mSettings.verbose); + if (errmsg.empty()) + return; + + // Alert only about unique errors. + // This makes sure the errors of a single check() call are unique. + // TODO: get rid of this? This is forwarded to another ErrorLogger which is also doing this + if (!mErrorList.emplace(std::move(errmsg)).second) + return; + + if (mAnalyzerInformation) + mAnalyzerInformation->reportErr(msg); + + if (!mSuppressions.nofail.isSuppressed(errorMessage) && !mSuppressions.nomsg.isSuppressed(errorMessage)) { + mExitCode = 1; + } + + std::string remark; + if (!msg.callStack.empty()) { + for (const auto& r: mRemarkComments) { + if (r.file != msg.callStack.back().getfile(false)) + continue; + if (r.lineNumber != msg.callStack.back().line) + continue; + remark = r.str; + break; + } + } + + if (!remark.empty()) { + ErrorMessage msg2(msg); + msg2.remark = std::move(remark); + mErrorLogger.reportErr(msg2); + } else { + mErrorLogger.reportErr(msg); + } + + // check if plistOutput should be populated and the current output file is open and the error is not suppressed + if (!mSettings.plistOutput.empty() && mPlistFile.is_open() && !mSuppressions.nomsg.isSuppressed(errorMessage)) { + // add error to plist output file + mPlistFile << ErrorLogger::plistData(msg); + } + } + + /** + * @brief Information about progress is directed here. + * + * @param outmsg Message to show, e.g. "Checking main.cpp..." + */ + void reportOut(const std::string &outmsg, Color c = Color::Reset) override + { + mErrorLogger.reportOut(outmsg, c); + } + + void reportProgress(const std::string &filename, const char stage[], const std::size_t value) override + { + mErrorLogger.reportProgress(filename, stage, value); + } + + ErrorLogger &mErrorLogger; + const Settings& mSettings; + Suppressions& mSuppressions; + bool mUseGlobalSuppressions; + + // TODO: store hashes instead of the full messages + std::unordered_set mErrorList; + + std::vector mRemarkComments; + + using Location = std::pair; + std::map> mLocationMacros; // What macros are used on a location? + + std::ofstream mPlistFile; + + unsigned int mExitCode{}; + + AnalyzerInformation* mAnalyzerInformation{}; +}; + + // File deleter namespace { class FilesDeleter { @@ -338,22 +534,21 @@ static std::string getDefinesFlags(const std::string &semicolonSeparatedString) CppCheck::CppCheck(ErrorLogger &errorLogger, bool useGlobalSuppressions, ExecuteCmdFn executeCommand) - : mErrorLogger(errorLogger) + : mLogger(new CppCheckLogger(errorLogger, mSettings, mSettings.supprs, useGlobalSuppressions)) + , mErrorLogger(*mLogger) + , mErrorLoggerDirect(errorLogger) , mUseGlobalSuppressions(useGlobalSuppressions) , mExecuteCommand(std::move(executeCommand)) {} CppCheck::~CppCheck() { + mLogger->setAnalyzerInfo(nullptr); + while (!mFileInfo.empty()) { delete mFileInfo.back(); mFileInfo.pop_back(); } - - if (mPlistFile.is_open()) { - mPlistFile << ErrorLogger::plistFooter(); - mPlistFile.close(); - } } const char * CppCheck::version() @@ -424,8 +619,11 @@ std::string CppCheck::getLibraryDumpData() const { return out; } +// TODO: clear error list before returning unsigned int CppCheck::checkClang(const FileWithDetails &file) { + // TODO: clear exitcode + if (mSettings.checks.isEnabled(Checks::unusedFunction) && !mUnusedFunctionsCheck) mUnusedFunctionsCheck.reset(new CheckUnusedFunctions()); @@ -491,14 +689,14 @@ unsigned int CppCheck::checkClang(const FileWithDetails &file) if (!mSettings.buildDir.empty()) { std::ifstream fin(clangStderr); auto reportError = [this](const ErrorMessage& errorMessage) { - reportErr(errorMessage); + mErrorLogger.reportErr(errorMessage); }; if (reportClangErrors(fin, reportError, compilerWarnings)) return 0; // TODO: report as failure? } else { std::istringstream istr(output2); auto reportError = [this](const ErrorMessage& errorMessage) { - reportErr(errorMessage); + mErrorLogger.reportErr(errorMessage); }; if (reportClangErrors(istr, reportError, compilerWarnings)) return 0; // TODO: report as failure? @@ -510,13 +708,13 @@ unsigned int CppCheck::checkClang(const FileWithDetails &file) } try { - Tokenizer tokenizer(mSettings, *this); + Tokenizer tokenizer(mSettings, mErrorLogger); tokenizer.list.appendFileIfNew(file.spath()); std::istringstream ast(output2); clangimport::parseClangAstDump(tokenizer, ast); ValueFlow::setValues(tokenizer.list, const_cast(*tokenizer.getSymbolDatabase()), - *this, + mErrorLogger, mSettings, &s_timerResults); if (mSettings.debugnormal) @@ -531,7 +729,7 @@ unsigned int CppCheck::checkClang(const FileWithDetails &file) // TODO: use tinyxml2 to create XML fdump << "\n"; for (const ErrorMessage& errmsg: compilerWarnings) - fdump << " \n"; + fdump << " \n"; fdump << " \n"; fdump << " \n"; fdump << " \n"; @@ -548,15 +746,14 @@ unsigned int CppCheck::checkClang(const FileWithDetails &file) } catch (const InternalError &e) { const ErrorMessage errmsg = ErrorMessage::fromInternalError(e, nullptr, file.spath(), "Bailing out from analysis: Processing Clang AST dump failed"); - reportErr(errmsg); + mErrorLogger.reportErr(errmsg); } catch (const TerminateException &) { // Analysis is terminated - return mExitCode; } catch (const std::exception &e) { internalError(file.spath(), std::string("Processing Clang AST dump failed: ") + e.what()); } - return mExitCode; + return mLogger->exitcode(); } unsigned int CppCheck::check(const FileWithDetails &file) @@ -579,7 +776,8 @@ unsigned int CppCheck::check(const FileSettings &fs) if (mSettings.checks.isEnabled(Checks::unusedFunction) && !mUnusedFunctionsCheck) mUnusedFunctionsCheck.reset(new CheckUnusedFunctions()); - CppCheck temp(mErrorLogger, mUseGlobalSuppressions, mExecuteCommand); + // need to pass the externally provided ErrorLogger instead of our internal wrapper + CppCheck temp(mErrorLoggerDirect, mUseGlobalSuppressions, mExecuteCommand); temp.mSettings = mSettings; if (!temp.mSettings.userDefines.empty()) temp.mSettings.userDefines += ';'; @@ -644,10 +842,10 @@ unsigned int CppCheck::checkFile(const FileWithDetails& file, const std::string if (mSettings.checks.isEnabled(Checks::unusedFunction) && !mUnusedFunctionsCheck) mUnusedFunctionsCheck.reset(new CheckUnusedFunctions()); - mExitCode = 0; + mLogger->resetExitCode(); if (Settings::terminated()) - return mExitCode; + return mLogger->exitcode(); const Timer fileTotalTimer(mSettings.showtime == SHOWTIME_MODES::SHOWTIME_FILE_TOTAL, file.spath()); @@ -672,27 +870,27 @@ unsigned int CppCheck::checkFile(const FileWithDetails& file, const std::string } } - if (mPlistFile.is_open()) { - mPlistFile << ErrorLogger::plistFooter(); - mPlistFile.close(); - } + mLogger->closePlist(); try { if (mSettings.library.markupFile(file.spath())) { if (!mSettings.buildDir.empty()) + { mAnalyzerInformation.reset(new AnalyzerInformation); + mLogger->setAnalyzerInfo(mAnalyzerInformation.get()); + } if (mUnusedFunctionsCheck && (mSettings.useSingleJob() || mAnalyzerInformation)) { std::size_t hash = 0; // this is not a real source file - we just want to tokenize it. treat it as C anyways as the language needs to be determined. - Tokenizer tokenizer(mSettings, *this); + Tokenizer tokenizer(mSettings, mErrorLogger); // enforce the language since markup files are special and do not adhere to the enforced language tokenizer.list.setLang(Standards::Language::C, true); if (fileStream) { std::vector files{file.spath()}; simplecpp::TokenList tokens(*fileStream, files); if (mAnalyzerInformation) { - const Preprocessor preprocessor(mSettings, *this); + const Preprocessor preprocessor(mSettings, mErrorLogger); hash = calculateHash(preprocessor, tokens, mSettings); } tokenizer.list.createTokens(std::move(tokens)); @@ -701,7 +899,7 @@ unsigned int CppCheck::checkFile(const FileWithDetails& file, const std::string std::vector files{file.spath()}; simplecpp::TokenList tokens(file.spath(), files); if (mAnalyzerInformation) { - const Preprocessor preprocessor(mSettings, *this); + const Preprocessor preprocessor(mSettings, mErrorLogger); hash = calculateHash(preprocessor, tokens, mSettings); } tokenizer.list.createTokens(std::move(tokens)); @@ -709,10 +907,13 @@ unsigned int CppCheck::checkFile(const FileWithDetails& file, const std::string mUnusedFunctionsCheck->parseTokens(tokenizer, mSettings); if (mAnalyzerInformation) { + mLogger->setAnalyzerInfo(nullptr); + std::list errors; mAnalyzerInformation->analyzeFile(mSettings.buildDir, file.spath(), cfgname, hash, errors); mAnalyzerInformation->setFileInfo("CheckUnusedFunctions", mUnusedFunctionsCheck->analyzerInfo()); mAnalyzerInformation->close(); + mAnalyzerInformation.reset(); } } return EXIT_SUCCESS; @@ -740,14 +941,14 @@ unsigned int CppCheck::checkFile(const FileWithDetails& file, const std::string output.msg, "syntaxError", Certainty::normal); - reportErr(errmsg); - return mExitCode; + mErrorLogger.reportErr(errmsg); + return mLogger->exitcode(); } - Preprocessor preprocessor(mSettings, *this); + Preprocessor preprocessor(mSettings, mErrorLogger); if (!preprocessor.loadFiles(tokens1, files)) - return mExitCode; + return mLogger->exitcode(); if (!mSettings.plistOutput.empty()) { std::string filename2; @@ -757,8 +958,7 @@ unsigned int CppCheck::checkFile(const FileWithDetails& file, const std::string filename2 = file.spath(); const std::size_t fileNameHash = std::hash {}(file.spath()); filename2 = mSettings.plistOutput + filename2.substr(0, filename2.find('.')) + "_" + std::to_string(fileNameHash) + ".plist"; - mPlistFile.open(filename2); - mPlistFile << ErrorLogger::plistHeader(version(), files); + mLogger->openPlist(filename2, files); } std::string dumpProlog; @@ -767,7 +967,7 @@ unsigned int CppCheck::checkFile(const FileWithDetails& file, const std::string } // Parse comments and then remove them - mRemarkComments = preprocessor.getRemarkComments(tokens1); + mLogger->setRemarkComments(preprocessor.getRemarkComments(tokens1)); preprocessor.inlineSuppressions(tokens1, mSettings.supprs.nomsg); if (mSettings.dump || !mSettings.addons.empty()) { std::ostringstream oss; @@ -776,8 +976,10 @@ unsigned int CppCheck::checkFile(const FileWithDetails& file, const std::string } preprocessor.removeComments(tokens1); - if (!mSettings.buildDir.empty()) + if (!mSettings.buildDir.empty()) { mAnalyzerInformation.reset(new AnalyzerInformation); + mLogger->setAnalyzerInfo(mAnalyzerInformation.get()); + } if (mAnalyzerInformation) { // Calculate hash so it can be compared with old hash / future hashes @@ -785,10 +987,10 @@ unsigned int CppCheck::checkFile(const FileWithDetails& file, const std::string std::list errors; if (!mAnalyzerInformation->analyzeFile(mSettings.buildDir, file.spath(), cfgname, hash, errors)) { while (!errors.empty()) { - reportErr(errors.front()); + mErrorLogger.reportErr(errors.front()); errors.pop_front(); } - return mExitCode; // known results => no need to reanalyze file + return mLogger->exitcode(); // known results => no need to reanalyze file } } @@ -894,11 +1096,11 @@ unsigned int CppCheck::checkFile(const FileWithDetails& file, const std::string pos = 0; while ((pos = codeWithoutCfg.find(Preprocessor::macroChar,pos)) != std::string::npos) codeWithoutCfg[pos] = ' '; - reportOut(codeWithoutCfg); + mErrorLogger.reportOut(codeWithoutCfg); continue; } - Tokenizer tokenizer(mSettings, *this); + Tokenizer tokenizer(mSettings, mErrorLogger); if (mSettings.showtime != SHOWTIME_MODES::SHOWTIME_NONE) tokenizer.setTimerResults(&s_timerResults); tokenizer.setDirectives(directives); // TODO: how to avoid repeated copies? @@ -912,11 +1114,7 @@ unsigned int CppCheck::checkFile(const FileWithDetails& file, const std::string hasValidConfig = true; // locations macros - mLocationMacros.clear(); - for (const Token* tok = tokenizer.tokens(); tok; tok = tok->next()) { - if (!tok->getMacroName().empty()) - mLocationMacros[Location(files[tok->fileIndex()], tok->linenr())].emplace(tok->getMacroName()); - } + mLogger->setLocationMacros(tokenizer.tokens(), files); // If only errors are printed, print filename after the check if (!mSettings.quiet && (!mCurrentConfig.empty() || checkCount > 1)) { @@ -988,17 +1186,16 @@ unsigned int CppCheck::checkFile(const FileWithDetails& file, const std::string o.msg, "preprocessorErrorDirective", Certainty::normal); - reportErr(errmsg); + mErrorLogger.reportErr(errmsg); } continue; } catch (const TerminateException &) { // Analysis is terminated - return mExitCode; - + return mLogger->exitcode(); } catch (const InternalError &e) { ErrorMessage errmsg = ErrorMessage::fromInternalError(e, &tokenizer.list, file.spath()); - reportErr(errmsg); + mErrorLogger.reportErr(errmsg); } } @@ -1017,7 +1214,7 @@ unsigned int CppCheck::checkFile(const FileWithDetails& file, const std::string msg, "noValidConfiguration", Certainty::normal); - reportErr(errmsg); + mErrorLogger.reportErr(errmsg); } // dumped all configs, close root element now @@ -1030,32 +1227,34 @@ unsigned int CppCheck::checkFile(const FileWithDetails& file, const std::string } catch (const TerminateException &) { // Analysis is terminated - return mExitCode; + return mLogger->exitcode(); } catch (const std::runtime_error &e) { internalError(file.spath(), std::string("Checking file failed: ") + e.what()); } catch (const std::bad_alloc &) { internalError(file.spath(), "Checking file failed: out of memory"); } catch (const InternalError &e) { const ErrorMessage errmsg = ErrorMessage::fromInternalError(e, nullptr, file.spath(), "Bailing out from analysis: Checking file failed"); - reportErr(errmsg); + mErrorLogger.reportErr(errmsg); } if (mAnalyzerInformation) { + mLogger->setAnalyzerInfo(nullptr); mAnalyzerInformation.reset(); } // In jointSuppressionReport mode, unmatched suppressions are // collected after all files are processed if (!mSettings.useSingleJob() && (mSettings.severity.isEnabled(Severity::information) || mSettings.checkConfiguration)) { - SuppressionList::reportUnmatchedSuppressions(mSettings.supprs.nomsg.getUnmatchedLocalSuppressions(file, (bool)mUnusedFunctionsCheck), *this); + SuppressionList::reportUnmatchedSuppressions(mSettings.supprs.nomsg.getUnmatchedLocalSuppressions(file, (bool)mUnusedFunctionsCheck), mErrorLogger); } - mErrorList.clear(); + // TODO: clear earlier? + mLogger->clear(); if (mSettings.showtime == SHOWTIME_MODES::SHOWTIME_FILE || mSettings.showtime == SHOWTIME_MODES::SHOWTIME_TOP5_FILE) printTimerResults(mSettings.showtime); - return mExitCode; + return mLogger->exitcode(); } // TODO: replace with ErrorMessage::fromInternalError() @@ -1106,13 +1305,13 @@ void CppCheck::checkNormalTokens(const Tokenizer &tokenizer) "Checks maximum time exceeded", "checksMaxTime", Certainty::normal); - reportErr(errmsg); + mErrorLogger.reportErr(errmsg); } return; } Timer::run(check->name() + "::runChecks", mSettings.showtime, &s_timerResults, [&]() { - check->runChecks(tokenizer, this); + check->runChecks(tokenizer, &mErrorLogger); }); } } @@ -1322,7 +1521,7 @@ void CppCheck::executeRules(const std::string &tokenlist, const TokenList &list) continue; if (!mSettings.quiet) { - reportOut("Processing rule: " + rule.pattern, Color::FgGreen); + mErrorLogger.reportOut("Processing rule: " + rule.pattern, Color::FgGreen); } const char *pcreCompileErrorStr = nullptr; @@ -1338,7 +1537,7 @@ void CppCheck::executeRules(const std::string &tokenlist, const TokenList &list) "pcre_compile", Certainty::normal); - reportErr(errmsg); + mErrorLogger.reportErr(errmsg); } continue; } @@ -1359,7 +1558,7 @@ void CppCheck::executeRules(const std::string &tokenlist, const TokenList &list) "pcre_study", Certainty::normal); - reportErr(errmsg); + mErrorLogger.reportErr(errmsg); // pcre_compile() worked, but pcre_study() returned an error. Free the resources allocated by pcre_compile(). pcre_free(re); continue; @@ -1382,7 +1581,7 @@ void CppCheck::executeRules(const std::string &tokenlist, const TokenList &list) "pcre_exec", Certainty::normal); - reportErr(errmsg); + mErrorLogger.reportErr(errmsg); } break; } @@ -1419,7 +1618,7 @@ void CppCheck::executeRules(const std::string &tokenlist, const TokenList &list) Certainty::normal); // Report error - reportErr(errmsg); + mErrorLogger.reportErr(errmsg); } pcre_free(re); @@ -1488,7 +1687,7 @@ void CppCheck::executeAddons(const std::vector& files, const std::s errmsg.severity = Severity::internal; errmsg.id = "ctuinfo"; errmsg.setmsg(res.serialize()); - reportErr(errmsg); + mErrorLogger.reportErr(errmsg); } continue; } @@ -1528,7 +1727,7 @@ void CppCheck::executeAddons(const std::vector& files, const std::s } errmsg.file0 = file0; - reportErr(errmsg); + mErrorLogger.reportErr(errmsg); } } @@ -1570,7 +1769,7 @@ void CppCheck::executeAddonsWholeProgram(const std::list &files executeAddons(ctuInfoFiles, ""); } catch (const InternalError& e) { const ErrorMessage errmsg = ErrorMessage::fromInternalError(e, nullptr, "", "Bailing out from analysis: Whole program analysis failed"); - reportErr(errmsg); + mErrorLogger.reportErr(errmsg); } } @@ -1615,7 +1814,7 @@ void CppCheck::tooManyConfigsError(const std::string &file, const int numberOfCo "toomanyconfigs", CWE398, Certainty::normal); - reportErr(errmsg); + mErrorLogger.reportErr(errmsg); } void CppCheck::purgedConfigurationMessage(const std::string &file, const std::string& configuration) @@ -1637,108 +1836,11 @@ void CppCheck::purgedConfigurationMessage(const std::string &file, const std::st "purgedConfiguration", Certainty::normal); - reportErr(errmsg); + mErrorLogger.reportErr(errmsg); } //--------------------------------------------------------------------------- -// TODO: part of this logic is duplicated in Executor::hasToLog() -void CppCheck::reportErr(const ErrorMessage &msg) -{ - if (msg.severity == Severity::internal) { - mErrorLogger.reportErr(msg); - return; - } - - if (!mSettings.library.reportErrors(msg.file0)) - return; - - std::set macroNames; - if (!msg.callStack.empty()) { - const std::string &file = msg.callStack.back().getfile(false); - int lineNumber = msg.callStack.back().line; - const auto it = mLocationMacros.find(Location(file, lineNumber)); - if (it != mLocationMacros.cend()) - macroNames = it->second; - } - - // TODO: only convert if necessary - const auto errorMessage = SuppressionList::ErrorMessage::fromErrorMessage(msg, macroNames); - - if (mSettings.supprs.nomsg.isSuppressed(errorMessage, mUseGlobalSuppressions)) { - // Safety: Report critical errors to ErrorLogger - if (mSettings.safety && ErrorLogger::isCriticalErrorId(msg.id)) { - mExitCode = 1; - - if (mSettings.supprs.nomsg.isSuppressedExplicitly(errorMessage, mUseGlobalSuppressions)) { - // Report with internal severity to signal that there is this critical error but - // it is suppressed - ErrorMessage temp(msg); - temp.severity = Severity::internal; - mErrorLogger.reportErr(temp); - } else { - // Report critical error that is not explicitly suppressed - mErrorLogger.reportErr(msg); - } - } - return; - } - - // TODO: there should be no need for the verbose and default messages here - std::string errmsg = msg.toString(mSettings.verbose); - if (errmsg.empty()) - return; - - // Alert only about unique errors. - // This makes sure the errors of a single check() call are unique. - // TODO: get rid of this? This is forwarded to another ErrorLogger which is also doing this - if (!mErrorList.emplace(std::move(errmsg)).second) - return; - - if (mAnalyzerInformation) - mAnalyzerInformation->reportErr(msg); - - if (!mSettings.supprs.nofail.isSuppressed(errorMessage) && !mSettings.supprs.nomsg.isSuppressed(errorMessage)) { - mExitCode = 1; - } - - std::string remark; - if (!msg.callStack.empty()) { - for (const auto& r: mRemarkComments) { - if (r.file != msg.callStack.back().getfile(false)) - continue; - if (r.lineNumber != msg.callStack.back().line) - continue; - remark = r.str; - break; - } - } - - if (!remark.empty()) { - ErrorMessage msg2(msg); - msg2.remark = std::move(remark); - mErrorLogger.reportErr(msg2); - } else { - mErrorLogger.reportErr(msg); - } - - // check if plistOutput should be populated and the current output file is open and the error is not suppressed - if (!mSettings.plistOutput.empty() && mPlistFile.is_open() && !mSettings.supprs.nomsg.isSuppressed(errorMessage)) { - // add error to plist output file - mPlistFile << ErrorLogger::plistData(msg); - } -} - -void CppCheck::reportOut(const std::string &outmsg, Color c) -{ - mErrorLogger.reportOut(outmsg, c); -} - -void CppCheck::reportProgress(const std::string &filename, const char stage[], const std::size_t value) -{ - mErrorLogger.reportProgress(filename, stage, value); -} - void CppCheck::getErrorMessages(ErrorLogger &errorlogger) { Settings s; @@ -1831,7 +1933,7 @@ void CppCheck::analyseClangTidy(const FileSettings &fileSettings) errmsg.file0 = std::move(fixedpath); errmsg.setmsg(messageString); - reportErr(errmsg); + mErrorLogger.reportErr(errmsg); } } @@ -1853,19 +1955,19 @@ bool CppCheck::analyseWholeProgram() // cppcheck-suppress shadowFunction - TODO: fix this for (Check *check : Check::instances()) - errors |= check->analyseWholeProgram(&ctu, mFileInfo, mSettings, *this); // TODO: ctu + errors |= check->analyseWholeProgram(&ctu, mFileInfo, mSettings, mErrorLogger); // TODO: ctu if (mUnusedFunctionsCheck) - errors |= mUnusedFunctionsCheck->check(mSettings, *this); + errors |= mUnusedFunctionsCheck->check(mSettings, mErrorLogger); - return errors && (mExitCode > 0); + return errors && (mLogger->exitcode() > 0); } unsigned int CppCheck::analyseWholeProgram(const std::string &buildDir, const std::list &files, const std::list& fileSettings, const std::string& ctuInfo) { executeAddonsWholeProgram(files, fileSettings, ctuInfo); if (mSettings.checks.isEnabled(Checks::unusedFunction)) - CheckUnusedFunctions::analyseWholeProgram(mSettings, *this, buildDir); + CheckUnusedFunctions::analyseWholeProgram(mSettings, mErrorLogger, buildDir); std::list fileInfoList; CTU::FileInfo ctuFileInfo; @@ -1913,15 +2015,15 @@ unsigned int CppCheck::analyseWholeProgram(const std::string &buildDir, const st // Analyse the tokens // cppcheck-suppress shadowFunction - TODO: fix this for (Check *check : Check::instances()) - check->analyseWholeProgram(&ctuFileInfo, fileInfoList, mSettings, *this); + check->analyseWholeProgram(&ctuFileInfo, fileInfoList, mSettings, mErrorLogger); if (mUnusedFunctionsCheck) - mUnusedFunctionsCheck->check(mSettings, *this); + mUnusedFunctionsCheck->check(mSettings, mErrorLogger); for (Check::FileInfo *fi : fileInfoList) delete fi; - return mExitCode; + return mLogger->exitcode(); } // cppcheck-suppress unusedFunction - only used in tests diff --git a/lib/cppcheck.h b/lib/cppcheck.h index fee5eccedac..23a3b8bb860 100644 --- a/lib/cppcheck.h +++ b/lib/cppcheck.h @@ -22,22 +22,15 @@ //--------------------------------------------------------------------------- #include "check.h" -#include "color.h" #include "config.h" -#include "errorlogger.h" #include "settings.h" -#include #include #include #include #include -#include #include -#include #include -#include -#include #include class TokenList; @@ -46,8 +39,8 @@ struct FileSettings; class CheckUnusedFunctions; class Tokenizer; class FileWithDetails; -class RemarkComment; class AnalyzerInformation; +class ErrorLogger; namespace simplecpp { class TokenList; } @@ -60,7 +53,7 @@ namespace simplecpp { class TokenList; } * errors or places that could be improved. * Usage: See check() for more info. */ -class CPPCHECKLIB CppCheck : ErrorLogger { +class CPPCHECKLIB CppCheck { public: using ExecuteCmdFn = std::function,std::string,std::string&)>; @@ -74,7 +67,7 @@ class CPPCHECKLIB CppCheck : ErrorLogger { /** * @brief Destructor. */ - ~CppCheck() override; + ~CppCheck(); /** * @brief This starts the actual checking. Note that you must call @@ -206,38 +199,18 @@ class CPPCHECKLIB CppCheck : ErrorLogger { unsigned int checkClang(const FileWithDetails &file); - /** - * @brief Errors and warnings are directed here. - * - * @param msg Errors messages are normally in format - * "[filepath:line number] Message", e.g. - * "[main.cpp:4] Uninitialized member variable" - */ - void reportErr(const ErrorMessage &msg) override; - - /** - * @brief Information about progress is directed here. - * - * @param outmsg Message to show, e.g. "Checking main.cpp..." - */ - void reportOut(const std::string &outmsg, Color c = Color::Reset) override; - - // TODO: store hashes instead of the full messages - std::unordered_set mErrorList; Settings mSettings; - void reportProgress(const std::string &filename, const char stage[], std::size_t value) override; - - ErrorLogger &mErrorLogger; + class CppCheckLogger; + std::unique_ptr mLogger; + /** the internal ErrorLogger */ + ErrorLogger& mErrorLogger; + /** the ErrorLogger provided to this instance */ + ErrorLogger& mErrorLoggerDirect; /** @brief Current preprocessor configuration */ std::string mCurrentConfig; - using Location = std::pair; - std::map> mLocationMacros; // What macros are used on a location? - - unsigned int mExitCode{}; - bool mUseGlobalSuppressions; /** Are there too many configs? */ @@ -251,11 +224,7 @@ class CPPCHECKLIB CppCheck : ErrorLogger { /** Callback for executing a shell command (exe, args, output) */ ExecuteCmdFn mExecuteCommand; - std::ofstream mPlistFile; - std::unique_ptr mUnusedFunctionsCheck; - - std::vector mRemarkComments; }; /// @}