LCOV - code coverage report
Current view: top level - lib - cppcheck.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 372 918 40.5 %
Date: 2024-04-28 12:00:40 Functions: 34 46 73.9 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Cppcheck - A tool for static C/C++ code analysis
       3             :  * Copyright (C) 2007-2024 Cppcheck team.
       4             :  *
       5             :  * This program is free software: you can redistribute it and/or modify
       6             :  * it under the terms of the GNU General Public License as published by
       7             :  * the Free Software Foundation, either version 3 of the License, or
       8             :  * (at your option) any later version.
       9             :  *
      10             :  * This program is distributed in the hope that it will be useful,
      11             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13             :  * GNU General Public License for more details.
      14             :  *
      15             :  * You should have received a copy of the GNU General Public License
      16             :  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
      17             :  */
      18             : 
      19             : #include "cppcheck.h"
      20             : 
      21             : #include "addoninfo.h"
      22             : #include "check.h"
      23             : #include "checkunusedfunctions.h"
      24             : #include "clangimport.h"
      25             : #include "color.h"
      26             : #include "ctu.h"
      27             : #include "errortypes.h"
      28             : #include "filesettings.h"
      29             : #include "library.h"
      30             : #include "path.h"
      31             : #include "platform.h"
      32             : #include "preprocessor.h"
      33             : #include "standards.h"
      34             : #include "suppressions.h"
      35             : #include "timer.h"
      36             : #include "token.h"
      37             : #include "tokenize.h"
      38             : #include "tokenlist.h"
      39             : #include "utils.h"
      40             : #include "valueflow.h"
      41             : #include "version.h"
      42             : 
      43             : #include <algorithm>
      44             : #include <cassert>
      45             : #include <cstdio>
      46             : #include <cstdint>
      47             : #include <cstring>
      48             : #include <cctype>
      49             : #include <cstdlib>
      50             : #include <ctime>
      51             : #include <exception> // IWYU pragma: keep
      52             : #include <fstream>
      53             : #include <iostream> // <- TEMPORARY
      54             : #include <new>
      55             : #include <set>
      56             : #include <sstream>
      57             : #include <stdexcept>
      58             : #include <string>
      59             : #include <unordered_set>
      60             : #include <utility>
      61             : #include <vector>
      62             : 
      63             : #include "json.h"
      64             : 
      65             : #include <simplecpp.h>
      66             : 
      67             : #include "xml.h"
      68             : 
      69             : #ifdef HAVE_RULES
      70             : #ifdef _WIN32
      71             : #define PCRE_STATIC
      72             : #endif
      73             : #include <pcre.h>
      74             : #endif
      75             : 
      76             : class SymbolDatabase;
      77             : 
      78             : static constexpr char Version[] = CPPCHECK_VERSION_STRING;
      79             : static constexpr char ExtraVersion[] = "";
      80             : 
      81             : static constexpr char FILELIST[] = "cppcheck-addon-ctu-file-list";
      82             : 
      83             : static TimerResults s_timerResults;
      84             : 
      85             : // CWE ids used
      86             : static const CWE CWE398(398U);  // Indicator of Poor Code Quality
      87             : 
      88             : // File deleter
      89             : namespace {
      90             :     class FilesDeleter {
      91             :     public:
      92        2355 :         FilesDeleter() = default;
      93        2355 :         ~FilesDeleter() {
      94        2355 :             for (const std::string& fileName: mFilenames)
      95           0 :                 std::remove(fileName.c_str());
      96        2355 :         }
      97           0 :         void addFile(const std::string& fileName) {
      98           0 :             mFilenames.push_back(fileName);
      99           0 :         }
     100             :     private:
     101             :         std::vector<std::string> mFilenames;
     102             :     };
     103             : }
     104             : 
     105           0 : static std::string cmdFileName(std::string f)
     106             : {
     107           0 :     f = Path::toNativeSeparators(std::move(f));
     108           0 :     if (f.find(' ') != std::string::npos)
     109           0 :         return "\"" + f + "\"";
     110           0 :     return f;
     111             : }
     112             : 
     113           4 : static std::vector<std::string> split(const std::string &str, const std::string &sep=" ")
     114             : {
     115           4 :     std::vector<std::string> ret;
     116          12 :     for (std::string::size_type startPos = 0U; startPos < str.size();) {
     117          10 :         startPos = str.find_first_not_of(sep, startPos);
     118          10 :         if (startPos == std::string::npos)
     119           2 :             break;
     120             : 
     121           8 :         if (str[startPos] == '\"') {
     122           2 :             const std::string::size_type endPos = str.find('\"', startPos + 1);
     123           2 :             ret.push_back(str.substr(startPos + 1, endPos - startPos - 1));
     124           2 :             startPos = (endPos < str.size()) ? (endPos + 1) : endPos;
     125           2 :             continue;
     126             :         }
     127             : 
     128           6 :         const std::string::size_type endPos = str.find(sep, startPos + 1);
     129           6 :         ret.push_back(str.substr(startPos, endPos - startPos));
     130           6 :         startPos = endPos;
     131             :     }
     132             : 
     133           4 :     return ret;
     134             : }
     135             : 
     136          25 : static std::string getDumpFileName(const Settings& settings, const std::string& filename)
     137             : {
     138          25 :     if (!settings.dumpFile.empty())
     139           0 :         return settings.dumpFile;
     140             : 
     141          50 :     std::string extension;
     142          25 :     if (settings.dump)
     143           0 :         extension = ".dump";
     144             :     else
     145          25 :         extension = "." + std::to_string(settings.pid) + ".dump";
     146             : 
     147          25 :     if (!settings.dump && !settings.buildDir.empty())
     148           0 :         return AnalyzerInformation::getAnalyzerInfoFile(settings.buildDir, filename, emptyString) + extension;
     149          25 :     return filename + extension;
     150             : }
     151             : 
     152          25 : static std::string getCtuInfoFileName(const std::string &dumpFile)
     153             : {
     154          50 :     return dumpFile.substr(0, dumpFile.size()-4) + "ctu-info";
     155             : }
     156             : 
     157        2355 : static void createDumpFile(const Settings& settings,
     158             :                            const std::string& filename,
     159             :                            std::ofstream& fdump,
     160             :                            std::string& dumpFile)
     161             : {
     162        2355 :     if (!settings.dump && settings.addons.empty())
     163        2352 :         return;
     164           0 :     dumpFile = getDumpFileName(settings, filename);
     165             : 
     166           0 :     fdump.open(dumpFile);
     167           0 :     if (!fdump.is_open())
     168           0 :         return;
     169             : 
     170             :     {
     171           0 :         std::ofstream fout(getCtuInfoFileName(dumpFile));
     172             :     }
     173             : 
     174           0 :     std::string language;
     175           0 :     switch (settings.enforcedLang) {
     176           0 :     case Standards::Language::C:
     177           0 :         language = " language=\"c\"";
     178           0 :         break;
     179           0 :     case Standards::Language::CPP:
     180           0 :         language = " language=\"cpp\"";
     181           0 :         break;
     182           0 :     case Standards::Language::None:
     183             :     {
     184             :         // TODO: error out on unknown language?
     185           0 :         const Standards::Language lang = Path::identify(filename);
     186           0 :         if (lang == Standards::Language::CPP)
     187           0 :             language = " language=\"cpp\"";
     188           0 :         else if (lang == Standards::Language::C)
     189           0 :             language = " language=\"c\"";
     190           0 :         break;
     191             :     }
     192             :     }
     193             : 
     194           0 :     fdump << "<?xml version=\"1.0\"?>\n";
     195           0 :     fdump << "<dumps" << language << ">\n";
     196             :     fdump << "  <platform"
     197             :           << " name=\"" << settings.platform.toString() << '\"'
     198           0 :           << " char_bit=\"" << settings.platform.char_bit << '\"'
     199           0 :           << " short_bit=\"" << settings.platform.short_bit << '\"'
     200           0 :           << " int_bit=\"" << settings.platform.int_bit << '\"'
     201           0 :           << " long_bit=\"" << settings.platform.long_bit << '\"'
     202           0 :           << " long_long_bit=\"" << settings.platform.long_long_bit << '\"'
     203           0 :           << " pointer_bit=\"" << (settings.platform.sizeof_pointer * settings.platform.char_bit) << '\"'
     204           0 :           << "/>" << '\n';
     205             : }
     206             : 
     207           0 : static std::string detectPython(const CppCheck::ExecuteCmdFn &executeCommand)
     208             : {
     209             : #ifdef _WIN32
     210             :     const char *py_exes[] = { "python3.exe", "python.exe" };
     211             : #else
     212           0 :     const char *py_exes[] = { "python3", "python" };
     213             : #endif
     214           0 :     for (const char* py_exe : py_exes) {
     215           0 :         std::string out;
     216             : #ifdef _MSC_VER
     217             :         // FIXME: hack to avoid debug assertion with _popen() in executeCommand() for non-existing commands
     218             :         const std::string cmd = std::string(py_exe) + " --version >NUL 2>&1";
     219             :         if (system(cmd.c_str()) != 0) {
     220             :             // TODO: get more detailed error?
     221             :             continue;
     222             :         }
     223             : #endif
     224           0 :         if (executeCommand(py_exe, split("--version"), "2>&1", out) == EXIT_SUCCESS && startsWith(out, "Python ") && std::isdigit(out[7])) {
     225           0 :             return py_exe;
     226             :         }
     227             :     }
     228           0 :     return "";
     229             : }
     230             : 
     231           0 : static std::vector<picojson::value> executeAddon(const AddonInfo &addonInfo,
     232             :                                                  const std::string &defaultPythonExe,
     233             :                                                  const std::string &file,
     234             :                                                  const std::string &premiumArgs,
     235             :                                                  const CppCheck::ExecuteCmdFn &executeCommand)
     236             : {
     237           0 :     const std::string redirect = "2>&1";
     238             : 
     239           0 :     std::string pythonExe;
     240             : 
     241           0 :     if (!addonInfo.executable.empty())
     242           0 :         pythonExe = addonInfo.executable;
     243           0 :     else if (!addonInfo.python.empty())
     244           0 :         pythonExe = cmdFileName(addonInfo.python);
     245           0 :     else if (!defaultPythonExe.empty())
     246           0 :         pythonExe = cmdFileName(defaultPythonExe);
     247             :     else {
     248             :         // store in static variable so we only look this up once
     249           0 :         static const std::string detectedPythonExe = detectPython(executeCommand);
     250           0 :         if (detectedPythonExe.empty())
     251           0 :             throw InternalError(nullptr, "Failed to auto detect python");
     252           0 :         pythonExe = detectedPythonExe;
     253             :     }
     254             : 
     255           0 :     std::string args;
     256           0 :     if (addonInfo.executable.empty())
     257           0 :         args = cmdFileName(addonInfo.runScript) + " " + cmdFileName(addonInfo.scriptFile);
     258           0 :     args += std::string(args.empty() ? "" : " ") + "--cli" + addonInfo.args;
     259           0 :     if (!premiumArgs.empty() && !addonInfo.executable.empty())
     260           0 :         args += " " + premiumArgs;
     261             : 
     262           0 :     const bool is_file_list = (file.find(FILELIST) != std::string::npos);
     263           0 :     const std::string fileArg = (is_file_list ? " --file-list " : " ") + cmdFileName(file);
     264           0 :     args += fileArg;
     265             : 
     266           0 :     std::string result;
     267           0 :     if (const int exitcode = executeCommand(pythonExe, split(args), redirect, result)) {
     268           0 :         std::string message("Failed to execute addon '" + addonInfo.name + "' - exitcode is " + std::to_string(exitcode));
     269           0 :         std::string details = pythonExe + " " + args;
     270           0 :         if (result.size() > 2) {
     271           0 :             details += "\nOutput:\n";
     272           0 :             details += result;
     273           0 :             const auto pos = details.find_last_not_of("\n\r");
     274           0 :             if (pos != std::string::npos)
     275           0 :                 details.resize(pos + 1);
     276             :         }
     277           0 :         throw InternalError(nullptr, std::move(message), std::move(details));
     278             :     }
     279             : 
     280           0 :     std::vector<picojson::value> addonResult;
     281             : 
     282             :     // Validate output..
     283           0 :     std::istringstream istr(result);
     284           0 :     std::string line;
     285           0 :     while (std::getline(istr, line)) {
     286             :         // TODO: also bail out?
     287           0 :         if (line.empty()) {
     288             :             //std::cout << "addon '" << addonInfo.name <<  "' result contains empty line" << std::endl;
     289           0 :             continue;
     290             :         }
     291             : 
     292             :         // TODO: get rid of this
     293           0 :         if (startsWith(line,"Checking ")) {
     294             :             //std::cout << "addon '" << addonInfo.name <<  "' result contains 'Checking ' line" << std::endl;
     295           0 :             continue;
     296             :         }
     297             : 
     298           0 :         if (line[0] != '{') {
     299             :             //std::cout << "addon '" << addonInfo.name <<  "' result is not a JSON" << std::endl;
     300             : 
     301           0 :             result.erase(result.find_last_not_of('\n') + 1, std::string::npos); // Remove trailing newlines
     302           0 :             throw InternalError(nullptr, "Failed to execute '" + pythonExe + " " + args + "'. " + result);
     303             :         }
     304             : 
     305             :         //std::cout << "addon '" << addonInfo.name <<  "' result is " << line << std::endl;
     306             : 
     307             :         // TODO: make these failures?
     308           0 :         picojson::value res;
     309           0 :         const std::string err = picojson::parse(res, line);
     310           0 :         if (!err.empty()) {
     311             :             //std::cout << "addon '" << addonInfo.name <<  "' result is not a valid JSON (" << err << ")" << std::endl;
     312           0 :             continue;
     313             :         }
     314           0 :         if (!res.is<picojson::object>()) {
     315             :             //std::cout << "addon '" << addonInfo.name <<  "' result is not a JSON object" << std::endl;
     316           0 :             continue;
     317             :         }
     318           0 :         addonResult.emplace_back(std::move(res));
     319             :     }
     320             : 
     321             :     // Valid results
     322           0 :     return addonResult;
     323             : }
     324             : 
     325           2 : static std::string getDefinesFlags(const std::string &semicolonSeparatedString)
     326             : {
     327           2 :     std::string flags;
     328           2 :     for (const std::string &d: split(semicolonSeparatedString, ";"))
     329           0 :         flags += "-D" + d + " ";
     330           2 :     return flags;
     331             : }
     332             : 
     333        2853 : CppCheck::CppCheck(ErrorLogger &errorLogger,
     334             :                    bool useGlobalSuppressions,
     335        2853 :                    ExecuteCmdFn executeCommand)
     336             :     : mErrorLogger(errorLogger)
     337             :     , mUseGlobalSuppressions(useGlobalSuppressions)
     338        2853 :     , mExecuteCommand(std::move(executeCommand))
     339        2854 : {}
     340             : 
     341        4134 : CppCheck::~CppCheck()
     342             : {
     343        2931 :     while (!mFileInfo.empty()) {
     344         864 :         delete mFileInfo.back();
     345         864 :         mFileInfo.pop_back();
     346             :     }
     347             : 
     348        2066 :     if (mPlistFile.is_open()) {
     349         401 :         mPlistFile << ErrorLogger::plistFooter();
     350         401 :         mPlistFile.close();
     351             :     }
     352        2067 : }
     353             : 
     354         615 : const char * CppCheck::version()
     355             : {
     356         615 :     return Version;
     357             : }
     358             : 
     359           5 : const char * CppCheck::extraVersion()
     360             : {
     361           5 :     return ExtraVersion;
     362             : }
     363             : 
     364           0 : static bool reportClangErrors(std::istream &is, const std::function<void(const ErrorMessage&)>& reportErr, std::vector<ErrorMessage> &warnings)
     365             : {
     366           0 :     std::string line;
     367           0 :     while (std::getline(is, line)) {
     368           0 :         if (line.empty() || line[0] == ' ' || line[0] == '`' || line[0] == '-')
     369           0 :             continue;
     370             : 
     371           0 :         std::string::size_type pos3 = line.find(": error: ");
     372           0 :         if (pos3 == std::string::npos)
     373           0 :             pos3 = line.find(": fatal error:");
     374           0 :         if (pos3 == std::string::npos)
     375           0 :             pos3 = line.find(": warning:");
     376           0 :         if (pos3 == std::string::npos)
     377           0 :             continue;
     378             : 
     379             :         // file:line:column: error: ....
     380           0 :         const std::string::size_type pos2 = line.rfind(':', pos3 - 1);
     381           0 :         const std::string::size_type pos1 = line.rfind(':', pos2 - 1);
     382             : 
     383           0 :         if (pos1 >= pos2 || pos2 >= pos3)
     384           0 :             continue;
     385             : 
     386           0 :         const std::string filename = line.substr(0, pos1);
     387           0 :         const std::string linenr = line.substr(pos1+1, pos2-pos1-1);
     388           0 :         const std::string colnr = line.substr(pos2+1, pos3-pos2-1);
     389           0 :         const std::string msg = line.substr(line.find(':', pos3+1) + 2);
     390             : 
     391           0 :         const std::string locFile = Path::toNativeSeparators(filename);
     392           0 :         const int line_i = strToInt<int>(linenr);
     393           0 :         const int column = strToInt<unsigned int>(colnr);
     394           0 :         ErrorMessage::FileLocation loc(locFile, line_i, column);
     395           0 :         ErrorMessage errmsg({std::move(loc)},
     396             :                             locFile,
     397             :                             Severity::error,
     398             :                             msg,
     399             :                             "syntaxError",
     400           0 :                             Certainty::normal);
     401             : 
     402           0 :         if (line.compare(pos3, 10, ": warning:") == 0) {
     403           0 :             warnings.push_back(std::move(errmsg));
     404           0 :             continue;
     405             :         }
     406             : 
     407           0 :         reportErr(errmsg);
     408             : 
     409           0 :         return true;
     410             :     }
     411           0 :     return false;
     412             : }
     413             : 
     414           0 : unsigned int CppCheck::checkClang(const std::string &path)
     415             : {
     416           0 :     if (mSettings.checks.isEnabled(Checks::unusedFunction) && !mUnusedFunctionsCheck)
     417           0 :         mUnusedFunctionsCheck.reset(new CheckUnusedFunctions());
     418             : 
     419           0 :     if (!mSettings.quiet)
     420           0 :         mErrorLogger.reportOut(std::string("Checking ") + path + " ...", Color::FgGreen);
     421             : 
     422             :     // TODO: this ignores the configured language
     423           0 :     const bool isCpp = Path::identify(path) == Standards::Language::CPP;
     424           0 :     const std::string langOpt = isCpp ? "-x c++" : "-x c";
     425           0 :     const std::string analyzerInfo = mSettings.buildDir.empty() ? std::string() : AnalyzerInformation::getAnalyzerInfoFile(mSettings.buildDir, path, emptyString);
     426           0 :     const std::string clangcmd = analyzerInfo + ".clang-cmd";
     427           0 :     const std::string clangStderr = analyzerInfo + ".clang-stderr";
     428           0 :     const std::string clangAst = analyzerInfo + ".clang-ast";
     429           0 :     std::string exe = mSettings.clangExecutable;
     430             : #ifdef _WIN32
     431             :     // append .exe if it is not a path
     432             :     if (Path::fromNativeSeparators(mSettings.clangExecutable).find('/') == std::string::npos) {
     433             :         exe += ".exe";
     434             :     }
     435             : #endif
     436             : 
     437           0 :     std::string flags(langOpt + " ");
     438             :     // TODO: does not apply C standard
     439           0 :     if (isCpp && !mSettings.standards.stdValue.empty())
     440           0 :         flags += "-std=" + mSettings.standards.stdValue + " ";
     441             : 
     442           0 :     for (const std::string &i: mSettings.includePaths)
     443           0 :         flags += "-I" + i + " ";
     444             : 
     445           0 :     flags += getDefinesFlags(mSettings.userDefines);
     446             : 
     447           0 :     const std::string args2 = "-fsyntax-only -Xclang -ast-dump -fno-color-diagnostics " + flags + path;
     448           0 :     const std::string redirect2 = analyzerInfo.empty() ? std::string("2>&1") : ("2> " + clangStderr);
     449           0 :     if (!mSettings.buildDir.empty()) {
     450           0 :         std::ofstream fout(clangcmd);
     451           0 :         fout << exe << " " << args2 << " " << redirect2 << std::endl;
     452           0 :     } else if (mSettings.verbose && !mSettings.quiet) {
     453           0 :         mErrorLogger.reportOut(exe + " " + args2);
     454             :     }
     455             : 
     456           0 :     std::string output2;
     457           0 :     const int exitcode = mExecuteCommand(exe,split(args2),redirect2,output2);
     458           0 :     if (exitcode != EXIT_SUCCESS) {
     459             :         // TODO: report as proper error
     460           0 :         std::cerr << "Failed to execute '" << exe << " " << args2 << " " << redirect2 << "' - (exitcode: " << exitcode << " / output: " << output2 << ")" << std::endl;
     461           0 :         return 0; // TODO: report as failure?
     462             :     }
     463             : 
     464           0 :     if (output2.find("TranslationUnitDecl") == std::string::npos) {
     465             :         // TODO: report as proper error
     466           0 :         std::cerr << "Failed to execute '" << exe << " " << args2 << " " << redirect2 << "' - (no TranslationUnitDecl in output)" << std::endl;
     467           0 :         return 0; // TODO: report as failure?
     468             :     }
     469             : 
     470             :     // Ensure there are not syntax errors...
     471           0 :     std::vector<ErrorMessage> compilerWarnings;
     472           0 :     if (!mSettings.buildDir.empty()) {
     473           0 :         std::ifstream fin(clangStderr);
     474           0 :         auto reportError = [this](const ErrorMessage& errorMessage) {
     475           0 :             reportErr(errorMessage);
     476           0 :         };
     477           0 :         if (reportClangErrors(fin, reportError, compilerWarnings))
     478           0 :             return 0;
     479             :     } else {
     480           0 :         std::istringstream istr(output2);
     481           0 :         auto reportError = [this](const ErrorMessage& errorMessage) {
     482           0 :             reportErr(errorMessage);
     483           0 :         };
     484           0 :         if (reportClangErrors(istr, reportError, compilerWarnings))
     485           0 :             return 0;
     486             :     }
     487             : 
     488           0 :     if (!mSettings.buildDir.empty()) {
     489           0 :         std::ofstream fout(clangAst);
     490           0 :         fout << output2 << std::endl;
     491             :     }
     492             : 
     493             :     try {
     494           0 :         Tokenizer tokenizer(mSettings, *this);
     495           0 :         tokenizer.list.appendFileIfNew(path);
     496           0 :         std::istringstream ast(output2);
     497           0 :         clangimport::parseClangAstDump(tokenizer, ast);
     498           0 :         ValueFlow::setValues(tokenizer.list,
     499           0 :                              const_cast<SymbolDatabase&>(*tokenizer.getSymbolDatabase()),
     500             :                              *this,
     501           0 :                              mSettings,
     502             :                              &s_timerResults);
     503           0 :         if (mSettings.debugnormal)
     504           0 :             tokenizer.printDebugOutput(1);
     505           0 :         checkNormalTokens(tokenizer);
     506             : 
     507             :         // create dumpfile
     508           0 :         std::ofstream fdump;
     509           0 :         std::string dumpFile;
     510           0 :         createDumpFile(mSettings, path, fdump, dumpFile);
     511           0 :         if (fdump.is_open()) {
     512             :             // TODO: use tinyxml2 to create XML
     513           0 :             fdump << "<dump cfg=\"\">\n";
     514           0 :             for (const ErrorMessage& errmsg: compilerWarnings)
     515           0 :                 fdump << "  <clang-warning file=\"" << toxml(errmsg.callStack.front().getfile()) << "\" line=\"" << errmsg.callStack.front().line << "\" column=\"" << errmsg.callStack.front().column << "\" message=\"" << toxml(errmsg.shortMessage()) << "\"/>\n";
     516           0 :             fdump << "  <standards>\n";
     517           0 :             fdump << "    <c version=\"" << mSettings.standards.getC() << "\"/>\n";
     518           0 :             fdump << "    <cpp version=\"" << mSettings.standards.getCPP() << "\"/>\n";
     519           0 :             fdump << "  </standards>\n";
     520           0 :             tokenizer.dump(fdump);
     521           0 :             fdump << "</dump>\n";
     522           0 :             fdump << "</dumps>\n";
     523           0 :             fdump.close();
     524             :         }
     525             : 
     526             :         // run addons
     527           0 :         executeAddons(dumpFile, path);
     528             : 
     529           0 :     } catch (const InternalError &e) {
     530           0 :         const ErrorMessage errmsg = ErrorMessage::fromInternalError(e, nullptr, path, "Bailing out from analysis: Processing Clang AST dump failed");
     531           0 :         reportErr(errmsg);
     532           0 :     } catch (const TerminateException &) {
     533             :         // Analysis is terminated
     534           0 :         return mExitCode;
     535           0 :     } catch (const std::exception &e) {
     536           0 :         internalError(path, std::string("Processing Clang AST dump failed: ") + e.what());
     537             :     }
     538             : 
     539           0 :     return mExitCode;
     540             : }
     541             : 
     542        1224 : unsigned int CppCheck::check(const std::string &path)
     543             : {
     544        1224 :     if (mSettings.clang)
     545           0 :         return checkClang(Path::simplifyPath(path));
     546             : 
     547        1224 :     return checkFile(Path::simplifyPath(path), emptyString);
     548             : }
     549             : 
     550           2 : unsigned int CppCheck::check(const std::string &path, const std::string &content)
     551             : {
     552           2 :     std::istringstream iss(content);
     553           4 :     return checkFile(Path::simplifyPath(path), emptyString, &iss);
     554             : }
     555             : 
     556        1138 : unsigned int CppCheck::check(const FileSettings &fs)
     557             : {
     558             :     // TODO: move to constructor when CppCheck no longer owns the settings
     559        1138 :     if (mSettings.checks.isEnabled(Checks::unusedFunction) && !mUnusedFunctionsCheck)
     560           1 :         mUnusedFunctionsCheck.reset(new CheckUnusedFunctions());
     561             : 
     562        2276 :     CppCheck temp(mErrorLogger, mUseGlobalSuppressions, mExecuteCommand);
     563        1138 :     temp.mSettings = mSettings;
     564        1138 :     if (!temp.mSettings.userDefines.empty())
     565           0 :         temp.mSettings.userDefines += ';';
     566        1138 :     if (mSettings.clang)
     567           0 :         temp.mSettings.userDefines += fs.defines;
     568             :     else
     569        1138 :         temp.mSettings.userDefines += fs.cppcheckDefines();
     570        1138 :     temp.mSettings.includePaths = fs.includePaths;
     571        1138 :     temp.mSettings.userUndefs.insert(fs.undefs.cbegin(), fs.undefs.cend());
     572        1138 :     if (fs.standard.find("++") != std::string::npos)
     573           0 :         temp.mSettings.standards.setCPP(fs.standard);
     574        1138 :     else if (!fs.standard.empty())
     575           0 :         temp.mSettings.standards.setC(fs.standard);
     576        1138 :     if (fs.platformType != Platform::Type::Unspecified)
     577           0 :         temp.mSettings.platform.set(fs.platformType);
     578        1138 :     if (mSettings.clang) {
     579           0 :         temp.mSettings.includePaths.insert(temp.mSettings.includePaths.end(), fs.systemIncludePaths.cbegin(), fs.systemIncludePaths.cend());
     580             :         // TODO: propagate back suppressions
     581           0 :         const unsigned int returnValue = temp.check(Path::simplifyPath(fs.filename()));
     582           0 :         if (mUnusedFunctionsCheck)
     583           0 :             mUnusedFunctionsCheck->updateFunctionData(*temp.mUnusedFunctionsCheck);
     584           0 :         return returnValue;
     585             :     }
     586        1138 :     const unsigned int returnValue = temp.checkFile(Path::simplifyPath(fs.filename()), fs.cfg);
     587        1138 :     mSettings.supprs.nomsg.addSuppressions(temp.mSettings.supprs.nomsg.getSuppressions());
     588        1138 :     if (mUnusedFunctionsCheck)
     589           1 :         mUnusedFunctionsCheck->updateFunctionData(*temp.mUnusedFunctionsCheck);
     590        1138 :     return returnValue;
     591             : }
     592             : 
     593        2357 : static simplecpp::TokenList createTokenList(const std::string& filename, std::vector<std::string>& files, simplecpp::OutputList* outputList, std::istream* fileStream)
     594             : {
     595        2357 :     if (fileStream)
     596           2 :         return {*fileStream, files, filename, outputList};
     597             : 
     598        2355 :     return {filename, files, outputList};
     599             : }
     600             : 
     601        2359 : unsigned int CppCheck::checkFile(const std::string& filename, const std::string &cfgname, std::istream* fileStream)
     602             : {
     603             :     // TODO: move to constructor when CppCheck no longer owns the settings
     604        2359 :     if (mSettings.checks.isEnabled(Checks::unusedFunction) && !mUnusedFunctionsCheck)
     605           2 :         mUnusedFunctionsCheck.reset(new CheckUnusedFunctions());
     606             : 
     607        2364 :     mExitCode = 0;
     608             : 
     609        2364 :     if (Settings::terminated())
     610           0 :         return mExitCode;
     611             : 
     612        4727 :     const Timer fileTotalTimer(mSettings.showtime == SHOWTIME_MODES::SHOWTIME_FILE_TOTAL, filename);
     613             : 
     614        2364 :     if (!mSettings.quiet) {
     615         466 :         std::string fixedpath = Path::simplifyPath(filename);
     616         233 :         fixedpath = Path::toNativeSeparators(std::move(fixedpath));
     617         233 :         mErrorLogger.reportOut(std::string("Checking ") + fixedpath + ' ' + cfgname + std::string("..."), Color::FgGreen);
     618             : 
     619         233 :         if (mSettings.verbose) {
     620           0 :             mErrorLogger.reportOut("Defines:" + mSettings.userDefines);
     621           0 :             std::string undefs;
     622           0 :             for (const std::string& U : mSettings.userUndefs) {
     623           0 :                 if (!undefs.empty())
     624           0 :                     undefs += ';';
     625           0 :                 undefs += ' ' + U;
     626             :             }
     627           0 :             mErrorLogger.reportOut("Undefines:" + undefs);
     628           0 :             std::string includePaths;
     629           0 :             for (const std::string &I : mSettings.includePaths)
     630           0 :                 includePaths += " -I" + I;
     631           0 :             mErrorLogger.reportOut("Includes:" + includePaths);
     632           0 :             mErrorLogger.reportOut(std::string("Platform:") + mSettings.platform.toString());
     633             :         }
     634             :     }
     635             : 
     636        2364 :     if (mPlistFile.is_open()) {
     637          99 :         mPlistFile << ErrorLogger::plistFooter();
     638          99 :         mPlistFile.close();
     639             :     }
     640             : 
     641             :     try {
     642        2364 :         if (mSettings.library.markupFile(filename)) {
     643           7 :             if (mUnusedFunctionsCheck && mSettings.useSingleJob() && mSettings.buildDir.empty()) {
     644             :                 // 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.
     645           0 :                 Tokenizer tokenizer(mSettings, *this);
     646           0 :                 tokenizer.list.setLang(Standards::Language::C);
     647           0 :                 if (fileStream) {
     648           0 :                     tokenizer.list.createTokens(*fileStream, filename);
     649             :                 }
     650             :                 else {
     651           0 :                     std::ifstream in(filename);
     652           0 :                     tokenizer.list.createTokens(in, filename);
     653             :                 }
     654           0 :                 mUnusedFunctionsCheck->parseTokens(tokenizer, mSettings);
     655             :             }
     656           9 :             return EXIT_SUCCESS;
     657             :         }
     658             : 
     659        2354 :         simplecpp::OutputList outputList;
     660        2354 :         std::vector<std::string> files;
     661        2353 :         simplecpp::TokenList tokens1 = createTokenList(filename, files, &outputList, fileStream);
     662             : 
     663             :         // If there is a syntax error, report it and stop
     664           2 :         const auto output_it = std::find_if(outputList.cbegin(), outputList.cend(), [](const simplecpp::Output &output){
     665           2 :             return Preprocessor::hasErrors(output);
     666        2357 :         });
     667        2355 :         if (output_it != outputList.cend()) {
     668           2 :             const simplecpp::Output &output = *output_it;
     669           4 :             std::string file = Path::fromNativeSeparators(output.location.file());
     670           2 :             if (mSettings.relativePaths)
     671           0 :                 file = Path::getRelativePath(file, mSettings.basePaths);
     672             : 
     673           4 :             ErrorMessage::FileLocation loc1(file, output.location.line, output.location.col);
     674             : 
     675           2 :             ErrorMessage errmsg({std::move(loc1)},
     676             :                                 "",
     677             :                                 Severity::error,
     678           2 :                                 output.msg,
     679             :                                 "syntaxError",
     680          12 :                                 Certainty::normal);
     681           2 :             reportErr(errmsg);
     682           2 :             return mExitCode;
     683             :         }
     684             : 
     685        2351 :         Preprocessor preprocessor(mSettings, *this);
     686             : 
     687        2355 :         if (!preprocessor.loadFiles(tokens1, files))
     688           0 :             return mExitCode;
     689             : 
     690        2355 :         if (!mSettings.plistOutput.empty()) {
     691        1200 :             std::string filename2;
     692         600 :             if (filename.find('/') != std::string::npos)
     693           0 :                 filename2 = filename.substr(filename.rfind('/') + 1);
     694             :             else
     695         600 :                 filename2 = filename;
     696         600 :             const std::size_t fileNameHash = std::hash<std::string> {}(filename);
     697         600 :             filename2 = mSettings.plistOutput + filename2.substr(0, filename2.find('.')) + "_" + std::to_string(fileNameHash) + ".plist";
     698         600 :             mPlistFile.open(filename2);
     699         600 :             mPlistFile << ErrorLogger::plistHeader(version(), files);
     700             :         }
     701             : 
     702        2355 :         std::string dumpProlog;
     703        2355 :         if (mSettings.dump || !mSettings.addons.empty()) {
     704           0 :             dumpProlog += getDumpFileContentsRawTokens(files, tokens1);
     705             :         }
     706             : 
     707             :         // Parse comments and then remove them
     708        2354 :         preprocessor.inlineSuppressions(tokens1, mSettings.supprs.nomsg);
     709        2354 :         if (mSettings.dump || !mSettings.addons.empty()) {
     710           0 :             std::ostringstream oss;
     711           0 :             mSettings.supprs.nomsg.dump(oss);
     712           0 :             dumpProlog += oss.str();
     713             :         }
     714        2355 :         tokens1.removeComments();
     715        2352 :         preprocessor.removeComments();
     716             : 
     717        2354 :         if (!mSettings.buildDir.empty()) {
     718             :             // Get toolinfo
     719           0 :             std::ostringstream toolinfo;
     720           0 :             toolinfo << CPPCHECK_VERSION_STRING;
     721           0 :             toolinfo << (mSettings.severity.isEnabled(Severity::warning) ? 'w' : ' ');
     722           0 :             toolinfo << (mSettings.severity.isEnabled(Severity::style) ? 's' : ' ');
     723           0 :             toolinfo << (mSettings.severity.isEnabled(Severity::performance) ? 'p' : ' ');
     724           0 :             toolinfo << (mSettings.severity.isEnabled(Severity::portability) ? 'p' : ' ');
     725           0 :             toolinfo << (mSettings.severity.isEnabled(Severity::information) ? 'i' : ' ');
     726           0 :             toolinfo << mSettings.userDefines;
     727           0 :             mSettings.supprs.nomsg.dump(toolinfo);
     728             : 
     729             :             // Calculate hash so it can be compared with old hash / future hashes
     730           0 :             const std::size_t hash = preprocessor.calculateHash(tokens1, toolinfo.str());
     731           0 :             std::list<ErrorMessage> errors;
     732           0 :             if (!mAnalyzerInformation.analyzeFile(mSettings.buildDir, filename, cfgname, hash, errors)) {
     733           0 :                 while (!errors.empty()) {
     734           0 :                     reportErr(errors.front());
     735           0 :                     errors.pop_front();
     736             :                 }
     737           0 :                 return mExitCode;  // known results => no need to reanalyze file
     738             :             }
     739             :         }
     740             : 
     741        2354 :         FilesDeleter filesDeleter;
     742             : 
     743             :         // write dump file xml prolog
     744        2354 :         std::ofstream fdump;
     745        2355 :         std::string dumpFile;
     746        2352 :         createDumpFile(mSettings, filename, fdump, dumpFile);
     747        2354 :         if (fdump.is_open()) {
     748           0 :             fdump << dumpProlog;
     749           0 :             if (!mSettings.dump)
     750           0 :                 filesDeleter.addFile(dumpFile);
     751             :         }
     752             : 
     753             :         // Get directives
     754        2355 :         std::list<Directive> directives = preprocessor.createDirectives(tokens1);
     755        2353 :         preprocessor.simplifyPragmaAsm(&tokens1);
     756             : 
     757        2353 :         preprocessor.setPlatformInfo(&tokens1);
     758             : 
     759             :         // Get configurations..
     760        2354 :         std::set<std::string> configurations;
     761        2353 :         if ((mSettings.checkAllConfigurations && mSettings.userDefines.empty()) || mSettings.force) {
     762        4708 :             Timer t("Preprocessor::getConfigs", mSettings.showtime, &s_timerResults);
     763        2355 :             configurations = preprocessor.getConfigs(tokens1);
     764             :         } else {
     765           0 :             configurations.insert(mSettings.userDefines);
     766             :         }
     767             : 
     768        2354 :         if (mSettings.checkConfiguration) {
     769           0 :             for (const std::string &config : configurations)
     770           0 :                 (void)preprocessor.getcode(tokens1, config, files, true);
     771             : 
     772           0 :             return 0;
     773             :         }
     774             : 
     775             : #ifdef HAVE_RULES
     776             :         // Run define rules on raw code
     777             :         if (hasRule("define")) {
     778             :             std::string code;
     779             :             for (const Directive &dir : directives) {
     780             :                 if (startsWith(dir.str,"#define ") || startsWith(dir.str,"#include "))
     781             :                     code += "#line " + std::to_string(dir.linenr) + " \"" + dir.file + "\"\n" + dir.str + '\n';
     782             :             }
     783             :             TokenList tokenlist(&mSettings);
     784             :             std::istringstream istr2(code);
     785             :             // TODO: asserts when file has unknown extension
     786             :             tokenlist.createTokens(istr2, Path::identify(*files.begin())); // TODO: check result?
     787             :             executeRules("define", tokenlist);
     788             :         }
     789             : #endif
     790             : 
     791        2354 :         if (!mSettings.force && configurations.size() > mSettings.maxConfigs) {
     792           0 :             if (mSettings.severity.isEnabled(Severity::information)) {
     793           0 :                 tooManyConfigsError(Path::toNativeSeparators(filename),configurations.size());
     794             :             } else {
     795           0 :                 mTooManyConfigs = true;
     796             :             }
     797             :         }
     798             : 
     799        2353 :         std::set<unsigned long long> hashes;
     800        2354 :         int checkCount = 0;
     801        2354 :         bool hasValidConfig = false;
     802        2354 :         std::list<std::string> configurationError;
     803        4732 :         for (const std::string &currCfg : configurations) {
     804             :             // bail out if terminated
     805        2377 :             if (Settings::terminated())
     806           0 :                 break;
     807             : 
     808             :             // Check only a few configurations (default 12), after that bail out, unless --force
     809             :             // was used.
     810        2377 :             if (!mSettings.force && ++checkCount > mSettings.maxConfigs)
     811           0 :                 break;
     812             : 
     813        2377 :             if (!mSettings.userDefines.empty()) {
     814           0 :                 mCurrentConfig = mSettings.userDefines;
     815           0 :                 const std::vector<std::string> v1(split(mSettings.userDefines, ";"));
     816           0 :                 for (const std::string &cfg: split(currCfg, ";")) {
     817           0 :                     if (std::find(v1.cbegin(), v1.cend(), cfg) == v1.cend()) {
     818           0 :                         mCurrentConfig += ";" + cfg;
     819             :                     }
     820             :                 }
     821             :             } else {
     822        2377 :                 mCurrentConfig = currCfg;
     823             :             }
     824             : 
     825        2376 :             if (mSettings.preprocessOnly) {
     826           0 :                 Timer t("Preprocessor::getcode", mSettings.showtime, &s_timerResults);
     827           0 :                 std::string codeWithoutCfg = preprocessor.getcode(tokens1, mCurrentConfig, files, true);
     828           0 :                 t.stop();
     829             : 
     830           0 :                 if (startsWith(codeWithoutCfg,"#file"))
     831           0 :                     codeWithoutCfg.insert(0U, "//");
     832           0 :                 std::string::size_type pos = 0;
     833           0 :                 while ((pos = codeWithoutCfg.find("\n#file",pos)) != std::string::npos)
     834           0 :                     codeWithoutCfg.insert(pos+1U, "//");
     835           0 :                 pos = 0;
     836           0 :                 while ((pos = codeWithoutCfg.find("\n#endfile",pos)) != std::string::npos)
     837           0 :                     codeWithoutCfg.insert(pos+1U, "//");
     838           0 :                 pos = 0;
     839           0 :                 while ((pos = codeWithoutCfg.find(Preprocessor::macroChar,pos)) != std::string::npos)
     840           0 :                     codeWithoutCfg[pos] = ' ';
     841           0 :                 reportOut(codeWithoutCfg);
     842           0 :                 continue;
     843             :             }
     844             : 
     845        2376 :             Tokenizer tokenizer(mSettings, *this);
     846        2378 :             if (mSettings.showtime != SHOWTIME_MODES::SHOWTIME_NONE)
     847         660 :                 tokenizer.setTimerResults(&s_timerResults);
     848        2378 :             tokenizer.setDirectives(directives); // TODO: how to avoid repeated copies?
     849             : 
     850             :             try {
     851             :                 // Create tokens, skip rest of iteration if failed
     852             :                 {
     853        7132 :                     Timer timer("Tokenizer::createTokens", mSettings.showtime, &s_timerResults);
     854        4756 :                     simplecpp::TokenList tokensP = preprocessor.preprocess(tokens1, mCurrentConfig, files, true);
     855        2378 :                     tokenizer.list.createTokens(std::move(tokensP));
     856             :                 }
     857        2378 :                 hasValidConfig = true;
     858             : 
     859             :                 // locations macros
     860        2378 :                 mLocationMacros.clear();
     861      333416 :                 for (const Token* tok = tokenizer.tokens(); tok; tok = tok->next()) {
     862      331046 :                     if (!tok->getMacroName().empty())
     863        7414 :                         mLocationMacros[Location(files[tok->fileIndex()], tok->linenr())].emplace(tok->getMacroName());
     864             :                 }
     865             : 
     866             :                 // If only errors are printed, print filename after the check
     867        2375 :                 if (!mSettings.quiet && (!mCurrentConfig.empty() || checkCount > 1)) {
     868          23 :                     std::string fixedpath = Path::simplifyPath(filename);
     869          23 :                     fixedpath = Path::toNativeSeparators(std::move(fixedpath));
     870          23 :                     mErrorLogger.reportOut("Checking " + fixedpath + ": " + mCurrentConfig + "...", Color::FgGreen);
     871             :                 }
     872             : 
     873        2375 :                 if (!tokenizer.tokens())
     874           0 :                     continue;
     875             : 
     876             :                 // skip rest of iteration if just checking configuration
     877        2376 :                 if (mSettings.checkConfiguration)
     878           0 :                     continue;
     879             : 
     880             : #ifdef HAVE_RULES
     881             :                 // Execute rules for "raw" code
     882             :                 executeRules("raw", tokenizer.list);
     883             : #endif
     884             : 
     885             :                 // Simplify tokens into normal form, skip rest of iteration if failed
     886        2376 :                 if (!tokenizer.simplifyTokens1(mCurrentConfig))
     887           0 :                     continue;
     888             : 
     889             :                 // dump xml if --dump
     890        2366 :                 if ((mSettings.dump || !mSettings.addons.empty()) && fdump.is_open()) {
     891           0 :                     fdump << "<dump cfg=\"" << ErrorLogger::toxml(mCurrentConfig) << "\">" << std::endl;
     892           0 :                     fdump << "  <standards>" << std::endl;
     893           0 :                     fdump << "    <c version=\"" << mSettings.standards.getC() << "\"/>" << std::endl;
     894           0 :                     fdump << "    <cpp version=\"" << mSettings.standards.getCPP() << "\"/>" << std::endl;
     895           0 :                     fdump << "  </standards>" << std::endl;
     896           0 :                     preprocessor.dump(fdump);
     897           0 :                     tokenizer.dump(fdump);
     898           0 :                     fdump << "</dump>" << std::endl;
     899             :                 }
     900             : 
     901             :                 // Need to call this even if the hash will skip this configuration
     902        2370 :                 mSettings.supprs.nomsg.markUnmatchedInlineSuppressionsAsChecked(tokenizer);
     903             : 
     904             :                 // Skip if we already met the same simplified token list
     905        2361 :                 if (mSettings.force || mSettings.maxConfigs > 1) {
     906        2361 :                     const std::size_t hash = tokenizer.list.calculateHash();
     907        2369 :                     if (hashes.find(hash) != hashes.end()) {
     908           5 :                         if (mSettings.debugwarnings)
     909           5 :                             purgedConfigurationMessage(filename, mCurrentConfig);
     910           5 :                         continue;
     911             :                     }
     912        2360 :                     hashes.insert(hash);
     913             :                 }
     914             : 
     915             :                 // Check normal tokens
     916        2365 :                 checkNormalTokens(tokenizer);
     917           8 :             } catch (const simplecpp::Output &o) {
     918             :                 // #error etc during preprocessing
     919           0 :                 configurationError.push_back((mCurrentConfig.empty() ? "\'\'" : mCurrentConfig) + " : [" + o.location.file() + ':' + std::to_string(o.location.line) + "] " + o.msg);
     920           0 :                 --checkCount; // don't count invalid configurations
     921             : 
     922           0 :                 if (!hasValidConfig && currCfg == *configurations.rbegin()) {
     923             :                     // If there is no valid configuration then report error..
     924           0 :                     std::string file = Path::fromNativeSeparators(o.location.file());
     925           0 :                     if (mSettings.relativePaths)
     926           0 :                         file = Path::getRelativePath(file, mSettings.basePaths);
     927             : 
     928           0 :                     ErrorMessage::FileLocation loc1(file, o.location.line, o.location.col);
     929             : 
     930           0 :                     ErrorMessage errmsg({std::move(loc1)},
     931             :                                         filename,
     932             :                                         Severity::error,
     933           0 :                                         o.msg,
     934             :                                         "preprocessorErrorDirective",
     935           0 :                                         Certainty::normal);
     936           0 :                     reportErr(errmsg);
     937             :                 }
     938           0 :                 continue;
     939             : 
     940           0 :             } catch (const TerminateException &) {
     941             :                 // Analysis is terminated
     942           0 :                 return mExitCode;
     943             : 
     944          16 :             } catch (const InternalError &e) {
     945          16 :                 ErrorMessage errmsg = ErrorMessage::fromInternalError(e, &tokenizer.list, filename);
     946           8 :                 reportErr(errmsg);
     947             :             }
     948             :         }
     949             : 
     950        2355 :         if (!hasValidConfig && configurations.size() > 1 && mSettings.severity.isEnabled(Severity::information)) {
     951           0 :             std::string msg;
     952           0 :             msg = "This file is not analyzed. Cppcheck failed to extract a valid configuration. Use -v for more details.";
     953           0 :             msg += "\nThis file is not analyzed. Cppcheck failed to extract a valid configuration. The tested configurations have these preprocessor errors:";
     954           0 :             for (const std::string &s : configurationError)
     955           0 :                 msg += '\n' + s;
     956             : 
     957           0 :             const std::string locFile = Path::toNativeSeparators(filename);
     958           0 :             ErrorMessage::FileLocation loc(locFile, 0, 0);
     959           0 :             ErrorMessage errmsg({std::move(loc)},
     960             :                                 locFile,
     961             :                                 Severity::information,
     962             :                                 msg,
     963             :                                 "noValidConfiguration",
     964           0 :                                 Certainty::normal);
     965           0 :             reportErr(errmsg);
     966             :         }
     967             : 
     968             :         // dumped all configs, close root </dumps> element now
     969        2355 :         if (fdump.is_open()) {
     970           0 :             fdump << "</dumps>" << std::endl;
     971           0 :             fdump.close();
     972             :         }
     973             : 
     974        2354 :         executeAddons(dumpFile, Path::simplifyPath(filename));
     975             : 
     976           0 :     } catch (const TerminateException &) {
     977             :         // Analysis is terminated
     978           0 :         return mExitCode;
     979           0 :     } catch (const std::runtime_error &e) {
     980           0 :         internalError(filename, std::string("Checking file failed: ") + e.what());
     981           0 :     } catch (const std::bad_alloc &) {
     982           0 :         internalError(filename, "Checking file failed: out of memory");
     983           0 :     } catch (const InternalError &e) {
     984           0 :         const ErrorMessage errmsg = ErrorMessage::fromInternalError(e, nullptr, filename, "Bailing out from analysis: Checking file failed");
     985           0 :         reportErr(errmsg);
     986             :     }
     987             : 
     988        2355 :     if (!mSettings.buildDir.empty()) {
     989           0 :         mAnalyzerInformation.close();
     990             :     }
     991             : 
     992             :     // In jointSuppressionReport mode, unmatched suppressions are
     993             :     // collected after all files are processed
     994        2355 :     if (!mSettings.useSingleJob() && (mSettings.severity.isEnabled(Severity::information) || mSettings.checkConfiguration)) {
     995         208 :         SuppressionList::reportUnmatchedSuppressions(mSettings.supprs.nomsg.getUnmatchedLocalSuppressions(filename, (bool)mUnusedFunctionsCheck), *this);
     996             :     }
     997             : 
     998        2355 :     mErrorList.clear();
     999             : 
    1000        2355 :     if (mSettings.showtime == SHOWTIME_MODES::SHOWTIME_FILE || mSettings.showtime == SHOWTIME_MODES::SHOWTIME_TOP5_FILE)
    1001          24 :         printTimerResults(mSettings.showtime);
    1002             : 
    1003        2355 :     return mExitCode;
    1004             : }
    1005             : 
    1006             : // TODO: replace with ErrorMessage::fromInternalError()
    1007           0 : void CppCheck::internalError(const std::string &filename, const std::string &msg)
    1008             : {
    1009           0 :     const std::string fullmsg("Bailing out from analysis: " + msg);
    1010             : 
    1011           0 :     ErrorMessage::FileLocation loc1(filename, 0, 0);
    1012             : 
    1013           0 :     ErrorMessage errmsg({std::move(loc1)},
    1014             :                         emptyString,
    1015             :                         Severity::error,
    1016             :                         fullmsg,
    1017             :                         "internalError",
    1018           0 :                         Certainty::normal);
    1019             : 
    1020           0 :     mErrorLogger.reportErr(errmsg);
    1021           0 : }
    1022             : 
    1023             : //---------------------------------------------------------------------------
    1024             : // CppCheck - A function that checks a normal token list
    1025             : //---------------------------------------------------------------------------
    1026             : 
    1027        2365 : void CppCheck::checkNormalTokens(const Tokenizer &tokenizer)
    1028             : {
    1029        2365 :     CheckUnusedFunctions unusedFunctionsChecker;
    1030             : 
    1031             :     // TODO: this should actually be the behavior if only "--enable=unusedFunction" is specified - see #10648
    1032        2359 :     const char* unusedFunctionOnly = std::getenv("UNUSEDFUNCTION_ONLY");
    1033        2365 :     const bool doUnusedFunctionOnly = unusedFunctionOnly && (std::strcmp(unusedFunctionOnly, "1") == 0);
    1034             : 
    1035        2365 :     if (!doUnusedFunctionOnly) {
    1036        2364 :         const std::time_t maxTime = mSettings.checksMaxTime > 0 ? std::time(nullptr) + mSettings.checksMaxTime : 0;
    1037             : 
    1038             :         // call all "runChecks" in all registered Check classes
    1039             :         // cppcheck-suppress shadowFunction - TODO: fix this
    1040       63851 :         for (Check *check : Check::instances()) {
    1041       61488 :             if (Settings::terminated())
    1042           0 :                 return;
    1043             : 
    1044       61488 :             if (maxTime > 0 && std::time(nullptr) > maxTime) {
    1045           0 :                 if (mSettings.debugwarnings) {
    1046           0 :                     ErrorMessage::FileLocation loc(tokenizer.list.getFiles()[0], 0, 0);
    1047           0 :                     ErrorMessage errmsg({std::move(loc)},
    1048             :                                         emptyString,
    1049             :                                         Severity::debug,
    1050             :                                         "Checks maximum time exceeded",
    1051             :                                         "checksMaxTime",
    1052           0 :                                         Certainty::normal);
    1053           0 :                     reportErr(errmsg);
    1054             :                 }
    1055           0 :                 return;
    1056             :             }
    1057             : 
    1058      122974 :             Timer timerRunChecks(check->name() + "::runChecks", mSettings.showtime, &s_timerResults);
    1059       61490 :             check->runChecks(tokenizer, this);
    1060             :         }
    1061             :     }
    1062             : 
    1063        2366 :     if (mSettings.checks.isEnabled(Checks::unusedFunction) && !mSettings.buildDir.empty()) {
    1064           0 :         unusedFunctionsChecker.parseTokens(tokenizer, mSettings);
    1065             :     }
    1066        2365 :     if (mUnusedFunctionsCheck && mSettings.useSingleJob() && mSettings.buildDir.empty()) {
    1067           2 :         mUnusedFunctionsCheck->parseTokens(tokenizer, mSettings);
    1068             :     }
    1069             : 
    1070        2365 :     if (mSettings.clang) {
    1071             :         // TODO: Use CTU for Clang analysis
    1072           0 :         return;
    1073             :     }
    1074             : 
    1075        2365 :     if (mSettings.useSingleJob() || !mSettings.buildDir.empty()) {
    1076             :         // Analyse the tokens..
    1077             : 
    1078         791 :         if (CTU::FileInfo * const fi1 = CTU::getFileInfo(tokenizer)) {
    1079         791 :             if (!mSettings.buildDir.empty())
    1080           0 :                 mAnalyzerInformation.setFileInfo("ctu", fi1->toString());
    1081         791 :             if (mSettings.useSingleJob())
    1082         791 :                 mFileInfo.push_back(fi1);
    1083             :             else
    1084           0 :                 delete fi1;
    1085             :         }
    1086             : 
    1087         791 :         if (!doUnusedFunctionOnly) {
    1088             :             // cppcheck-suppress shadowFunction - TODO: fix this
    1089       21357 :             for (const Check *check : Check::instances()) {
    1090       20566 :                 if (Check::FileInfo * const fi = check->getFileInfo(tokenizer, mSettings)) {
    1091          73 :                     if (!mSettings.buildDir.empty())
    1092           0 :                         mAnalyzerInformation.setFileInfo(check->name(), fi->toString());
    1093          73 :                     if (mSettings.useSingleJob())
    1094          73 :                         mFileInfo.push_back(fi);
    1095             :                     else
    1096           0 :                         delete fi;
    1097             :                 }
    1098             :             }
    1099             :         }
    1100             :     }
    1101             : 
    1102        2365 :     if (mSettings.checks.isEnabled(Checks::unusedFunction) && !mSettings.buildDir.empty()) {
    1103           0 :         mAnalyzerInformation.setFileInfo("CheckUnusedFunctions", unusedFunctionsChecker.analyzerInfo());
    1104             :     }
    1105             : 
    1106             : #ifdef HAVE_RULES
    1107             :     executeRules("normal", tokenizer.list);
    1108             : #endif
    1109             : }
    1110             : 
    1111             : //---------------------------------------------------------------------------
    1112             : 
    1113             : #ifdef HAVE_RULES
    1114             : bool CppCheck::hasRule(const std::string &tokenlist) const
    1115             : {
    1116             :     return std::any_of(mSettings.rules.cbegin(), mSettings.rules.cend(), [&](const Settings::Rule& rule) {
    1117             :         return rule.tokenlist == tokenlist;
    1118             :     });
    1119             : }
    1120             : 
    1121             : static const char * pcreErrorCodeToString(const int pcreExecRet)
    1122             : {
    1123             :     switch (pcreExecRet) {
    1124             :     case PCRE_ERROR_NULL:
    1125             :         return "Either code or subject was passed as NULL, or ovector was NULL "
    1126             :                "and ovecsize was not zero (PCRE_ERROR_NULL)";
    1127             :     case PCRE_ERROR_BADOPTION:
    1128             :         return "An unrecognized bit was set in the options argument (PCRE_ERROR_BADOPTION)";
    1129             :     case PCRE_ERROR_BADMAGIC:
    1130             :         return "PCRE stores a 4-byte \"magic number\" at the start of the compiled code, "
    1131             :                "to catch the case when it is passed a junk pointer and to detect when a "
    1132             :                "pattern that was compiled in an environment of one endianness is run in "
    1133             :                "an environment with the other endianness. This is the error that PCRE "
    1134             :                "gives when the magic number is not present (PCRE_ERROR_BADMAGIC)";
    1135             :     case PCRE_ERROR_UNKNOWN_NODE:
    1136             :         return "While running the pattern match, an unknown item was encountered in the "
    1137             :                "compiled pattern. This error could be caused by a bug in PCRE or by "
    1138             :                "overwriting of the compiled pattern (PCRE_ERROR_UNKNOWN_NODE)";
    1139             :     case PCRE_ERROR_NOMEMORY:
    1140             :         return "If a pattern contains back references, but the ovector that is passed "
    1141             :                "to pcre_exec() is not big enough to remember the referenced substrings, "
    1142             :                "PCRE gets a block of memory at the start of matching to use for this purpose. "
    1143             :                "If the call via pcre_malloc() fails, this error is given. The memory is "
    1144             :                "automatically freed at the end of matching. This error is also given if "
    1145             :                "pcre_stack_malloc() fails in pcre_exec(). "
    1146             :                "This can happen only when PCRE has been compiled with "
    1147             :                "--disable-stack-for-recursion (PCRE_ERROR_NOMEMORY)";
    1148             :     case PCRE_ERROR_NOSUBSTRING:
    1149             :         return "This error is used by the pcre_copy_substring(), pcre_get_substring(), "
    1150             :                "and pcre_get_substring_list() functions (see below). "
    1151             :                "It is never returned by pcre_exec() (PCRE_ERROR_NOSUBSTRING)";
    1152             :     case PCRE_ERROR_MATCHLIMIT:
    1153             :         return "The backtracking limit, as specified by the match_limit field in a pcre_extra "
    1154             :                "structure (or defaulted) was reached. "
    1155             :                "See the description above (PCRE_ERROR_MATCHLIMIT)";
    1156             :     case PCRE_ERROR_CALLOUT:
    1157             :         return "This error is never generated by pcre_exec() itself. "
    1158             :                "It is provided for use by callout functions that want to yield a distinctive "
    1159             :                "error code. See the pcrecallout documentation for details (PCRE_ERROR_CALLOUT)";
    1160             :     case PCRE_ERROR_BADUTF8:
    1161             :         return "A string that contains an invalid UTF-8 byte sequence was passed as a subject, "
    1162             :                "and the PCRE_NO_UTF8_CHECK option was not set. If the size of the output vector "
    1163             :                "(ovecsize) is at least 2, the byte offset to the start of the the invalid UTF-8 "
    1164             :                "character is placed in the first element, and a reason code is placed in the "
    1165             :                "second element. The reason codes are listed in the following section. For "
    1166             :                "backward compatibility, if PCRE_PARTIAL_HARD is set and the problem is a truncated "
    1167             :                "UTF-8 character at the end of the subject (reason codes 1 to 5), "
    1168             :                "PCRE_ERROR_SHORTUTF8 is returned instead of PCRE_ERROR_BADUTF8";
    1169             :     case PCRE_ERROR_BADUTF8_OFFSET:
    1170             :         return "The UTF-8 byte sequence that was passed as a subject was checked and found to "
    1171             :                "be valid (the PCRE_NO_UTF8_CHECK option was not set), but the value of "
    1172             :                "startoffset did not point to the beginning of a UTF-8 character or the end of "
    1173             :                "the subject (PCRE_ERROR_BADUTF8_OFFSET)";
    1174             :     case PCRE_ERROR_PARTIAL:
    1175             :         return "The subject string did not match, but it did match partially. See the "
    1176             :                "pcrepartial documentation for details of partial matching (PCRE_ERROR_PARTIAL)";
    1177             :     case PCRE_ERROR_BADPARTIAL:
    1178             :         return "This code is no longer in use. It was formerly returned when the PCRE_PARTIAL "
    1179             :                "option was used with a compiled pattern containing items that were not supported "
    1180             :                "for partial matching. From release 8.00 onwards, there are no restrictions on "
    1181             :                "partial matching (PCRE_ERROR_BADPARTIAL)";
    1182             :     case PCRE_ERROR_INTERNAL:
    1183             :         return "An unexpected internal error has occurred. This error could be caused by a bug "
    1184             :                "in PCRE or by overwriting of the compiled pattern (PCRE_ERROR_INTERNAL)";
    1185             :     case PCRE_ERROR_BADCOUNT:
    1186             :         return "This error is given if the value of the ovecsize argument is negative "
    1187             :                "(PCRE_ERROR_BADCOUNT)";
    1188             :     case PCRE_ERROR_RECURSIONLIMIT:
    1189             :         return "The internal recursion limit, as specified by the match_limit_recursion "
    1190             :                "field in a pcre_extra structure (or defaulted) was reached. "
    1191             :                "See the description above (PCRE_ERROR_RECURSIONLIMIT)";
    1192             :     case PCRE_ERROR_DFA_UITEM:
    1193             :         return "PCRE_ERROR_DFA_UITEM";
    1194             :     case PCRE_ERROR_DFA_UCOND:
    1195             :         return "PCRE_ERROR_DFA_UCOND";
    1196             :     case PCRE_ERROR_DFA_WSSIZE:
    1197             :         return "PCRE_ERROR_DFA_WSSIZE";
    1198             :     case PCRE_ERROR_DFA_RECURSE:
    1199             :         return "PCRE_ERROR_DFA_RECURSE";
    1200             :     case PCRE_ERROR_NULLWSLIMIT:
    1201             :         return "PCRE_ERROR_NULLWSLIMIT";
    1202             :     case PCRE_ERROR_BADNEWLINE:
    1203             :         return "An invalid combination of PCRE_NEWLINE_xxx options was "
    1204             :                "given (PCRE_ERROR_BADNEWLINE)";
    1205             :     case PCRE_ERROR_BADOFFSET:
    1206             :         return "The value of startoffset was negative or greater than the length "
    1207             :                "of the subject, that is, the value in length (PCRE_ERROR_BADOFFSET)";
    1208             :     case PCRE_ERROR_SHORTUTF8:
    1209             :         return "This error is returned instead of PCRE_ERROR_BADUTF8 when the subject "
    1210             :                "string ends with a truncated UTF-8 character and the PCRE_PARTIAL_HARD option is set. "
    1211             :                "Information about the failure is returned as for PCRE_ERROR_BADUTF8. "
    1212             :                "It is in fact sufficient to detect this case, but this special error code for "
    1213             :                "PCRE_PARTIAL_HARD precedes the implementation of returned information; "
    1214             :                "it is retained for backwards compatibility (PCRE_ERROR_SHORTUTF8)";
    1215             :     case PCRE_ERROR_RECURSELOOP:
    1216             :         return "This error is returned when pcre_exec() detects a recursion loop "
    1217             :                "within the pattern. Specifically, it means that either the whole pattern "
    1218             :                "or a subpattern has been called recursively for the second time at the same "
    1219             :                "position in the subject string. Some simple patterns that might do this "
    1220             :                "are detected and faulted at compile time, but more complicated cases, "
    1221             :                "in particular mutual recursions between two different subpatterns, "
    1222             :                "cannot be detected until run time (PCRE_ERROR_RECURSELOOP)";
    1223             :     case PCRE_ERROR_JIT_STACKLIMIT:
    1224             :         return "This error is returned when a pattern that was successfully studied "
    1225             :                "using a JIT compile option is being matched, but the memory available "
    1226             :                "for the just-in-time processing stack is not large enough. See the pcrejit "
    1227             :                "documentation for more details (PCRE_ERROR_JIT_STACKLIMIT)";
    1228             :     case PCRE_ERROR_BADMODE:
    1229             :         return "This error is given if a pattern that was compiled by the 8-bit library "
    1230             :                "is passed to a 16-bit or 32-bit library function, or vice versa (PCRE_ERROR_BADMODE)";
    1231             :     case PCRE_ERROR_BADENDIANNESS:
    1232             :         return "This error is given if a pattern that was compiled and saved is reloaded on a "
    1233             :                "host with different endianness. The utility function pcre_pattern_to_host_byte_order() "
    1234             :                "can be used to convert such a pattern so that it runs on the new host (PCRE_ERROR_BADENDIANNESS)";
    1235             :     case PCRE_ERROR_DFA_BADRESTART:
    1236             :         return "PCRE_ERROR_DFA_BADRESTART";
    1237             : #if PCRE_MAJOR >= 8 && PCRE_MINOR >= 32
    1238             :     case PCRE_ERROR_BADLENGTH:
    1239             :         return "This error is given if pcre_exec() is called with a negative value for the length argument (PCRE_ERROR_BADLENGTH)";
    1240             :     case PCRE_ERROR_JIT_BADOPTION:
    1241             :         return "This error is returned when a pattern that was successfully studied using a JIT compile "
    1242             :                "option is being matched, but the matching mode (partial or complete match) does not correspond "
    1243             :                "to any JIT compilation mode. When the JIT fast path function is used, this error may be "
    1244             :                "also given for invalid options. See the pcrejit documentation for more details (PCRE_ERROR_JIT_BADOPTION)";
    1245             : #endif
    1246             :     }
    1247             :     return "";
    1248             : }
    1249             : 
    1250             : void CppCheck::executeRules(const std::string &tokenlist, const TokenList &list)
    1251             : {
    1252             :     // There is no rule to execute
    1253             :     if (!hasRule(tokenlist))
    1254             :         return;
    1255             : 
    1256             :     // Write all tokens in a string that can be parsed by pcre
    1257             :     std::string str;
    1258             :     for (const Token *tok = list.front(); tok; tok = tok->next()) {
    1259             :         str += " ";
    1260             :         str += tok->str();
    1261             :     }
    1262             : 
    1263             :     for (const Settings::Rule &rule : mSettings.rules) {
    1264             :         if (rule.tokenlist != tokenlist)
    1265             :             continue;
    1266             : 
    1267             :         if (!mSettings.quiet) {
    1268             :             reportOut("Processing rule: " + rule.pattern, Color::FgGreen);
    1269             :         }
    1270             : 
    1271             :         const char *pcreCompileErrorStr = nullptr;
    1272             :         int erroffset = 0;
    1273             :         pcre * const re = pcre_compile(rule.pattern.c_str(),0,&pcreCompileErrorStr,&erroffset,nullptr);
    1274             :         if (!re) {
    1275             :             if (pcreCompileErrorStr) {
    1276             :                 const std::string msg = "pcre_compile failed: " + std::string(pcreCompileErrorStr);
    1277             :                 const ErrorMessage errmsg(std::list<ErrorMessage::FileLocation>(),
    1278             :                                           emptyString,
    1279             :                                           Severity::error,
    1280             :                                           msg,
    1281             :                                           "pcre_compile",
    1282             :                                           Certainty::normal);
    1283             : 
    1284             :                 reportErr(errmsg);
    1285             :             }
    1286             :             continue;
    1287             :         }
    1288             : 
    1289             :         // Optimize the regex, but only if PCRE_CONFIG_JIT is available
    1290             : #ifdef PCRE_CONFIG_JIT
    1291             :         const char *pcreStudyErrorStr = nullptr;
    1292             :         pcre_extra * const pcreExtra = pcre_study(re, PCRE_STUDY_JIT_COMPILE, &pcreStudyErrorStr);
    1293             :         // pcre_study() returns NULL for both errors and when it can not optimize the regex.
    1294             :         // The last argument is how one checks for errors.
    1295             :         // It is NULL if everything works, and points to an error string otherwise.
    1296             :         if (pcreStudyErrorStr) {
    1297             :             const std::string msg = "pcre_study failed: " + std::string(pcreStudyErrorStr);
    1298             :             const ErrorMessage errmsg(std::list<ErrorMessage::FileLocation>(),
    1299             :                                       emptyString,
    1300             :                                       Severity::error,
    1301             :                                       msg,
    1302             :                                       "pcre_study",
    1303             :                                       Certainty::normal);
    1304             : 
    1305             :             reportErr(errmsg);
    1306             :             // pcre_compile() worked, but pcre_study() returned an error. Free the resources allocated by pcre_compile().
    1307             :             pcre_free(re);
    1308             :             continue;
    1309             :         }
    1310             : #else
    1311             :         const pcre_extra * const pcreExtra = nullptr;
    1312             : #endif
    1313             : 
    1314             :         int pos = 0;
    1315             :         int ovector[30]= {0};
    1316             :         while (pos < (int)str.size()) {
    1317             :             const int pcreExecRet = pcre_exec(re, pcreExtra, str.c_str(), (int)str.size(), pos, 0, ovector, 30);
    1318             :             if (pcreExecRet < 0) {
    1319             :                 const std::string errorMessage = pcreErrorCodeToString(pcreExecRet);
    1320             :                 if (!errorMessage.empty()) {
    1321             :                     const ErrorMessage errmsg(std::list<ErrorMessage::FileLocation>(),
    1322             :                                               emptyString,
    1323             :                                               Severity::error,
    1324             :                                               std::string("pcre_exec failed: ") + errorMessage,
    1325             :                                               "pcre_exec",
    1326             :                                               Certainty::normal);
    1327             : 
    1328             :                     reportErr(errmsg);
    1329             :                 }
    1330             :                 break;
    1331             :             }
    1332             :             const auto pos1 = (unsigned int)ovector[0];
    1333             :             const auto pos2 = (unsigned int)ovector[1];
    1334             : 
    1335             :             // jump to the end of the match for the next pcre_exec
    1336             :             pos = (int)pos2;
    1337             : 
    1338             :             // determine location..
    1339             :             int fileIndex = 0;
    1340             :             int line = 0;
    1341             : 
    1342             :             std::size_t len = 0;
    1343             :             for (const Token *tok = list.front(); tok; tok = tok->next()) {
    1344             :                 len = len + 1U + tok->str().size();
    1345             :                 if (len > pos1) {
    1346             :                     fileIndex = tok->fileIndex();
    1347             :                     line = tok->linenr();
    1348             :                     break;
    1349             :                 }
    1350             :             }
    1351             : 
    1352             :             const std::string& file = list.getFiles()[fileIndex];
    1353             : 
    1354             :             ErrorMessage::FileLocation loc(file, line, 0);
    1355             : 
    1356             :             // Create error message
    1357             :             const ErrorMessage errmsg({std::move(loc)},
    1358             :                                       list.getSourceFilePath(),
    1359             :                                       rule.severity,
    1360             :                                       !rule.summary.empty() ? rule.summary : "found '" + str.substr(pos1, pos2 - pos1) + "'",
    1361             :                                       rule.id,
    1362             :                                       Certainty::normal);
    1363             : 
    1364             :             // Report error
    1365             :             reportErr(errmsg);
    1366             :         }
    1367             : 
    1368             :         pcre_free(re);
    1369             : #ifdef PCRE_CONFIG_JIT
    1370             :         // Free up the EXTRA PCRE value (may be NULL at this point)
    1371             :         if (pcreExtra) {
    1372             :             pcre_free_study(pcreExtra);
    1373             :         }
    1374             : #endif
    1375             :     }
    1376             : }
    1377             : #endif
    1378             : 
    1379        2355 : void CppCheck::executeAddons(const std::string& dumpFile, const std::string& file0)
    1380             : {
    1381        2355 :     if (!dumpFile.empty()) {
    1382           0 :         std::vector<std::string> f{dumpFile};
    1383           0 :         executeAddons(f, file0);
    1384             :     }
    1385        2355 : }
    1386             : 
    1387           0 : void CppCheck::executeAddons(const std::vector<std::string>& files, const std::string& file0)
    1388             : {
    1389           0 :     if (mSettings.addons.empty() || files.empty())
    1390           0 :         return;
    1391             : 
    1392           0 :     FilesDeleter filesDeleter;
    1393             : 
    1394           0 :     std::string fileList;
    1395             : 
    1396           0 :     if (files.size() >= 2 || endsWith(files[0], ".ctu-info")) {
    1397           0 :         fileList = Path::getPathFromFilename(files[0]) + FILELIST + std::to_string(mSettings.pid);
    1398           0 :         filesDeleter.addFile(fileList);
    1399           0 :         std::ofstream fout(fileList);
    1400           0 :         for (const std::string& f: files)
    1401           0 :             fout << f << std::endl;
    1402             :     }
    1403             : 
    1404             :     // ensure all addons have already been resolved - TODO: remove when settings are const after creation
    1405           0 :     assert(mSettings.addonInfos.size() == mSettings.addons.size());
    1406             : 
    1407           0 :     for (const AddonInfo &addonInfo : mSettings.addonInfos) {
    1408           0 :         if (addonInfo.name != "misra" && !addonInfo.ctu && endsWith(files.back(), ".ctu-info"))
    1409           0 :             continue;
    1410             : 
    1411             :         const std::vector<picojson::value> results =
    1412           0 :             executeAddon(addonInfo, mSettings.addonPython, fileList.empty() ? files[0] : fileList, mSettings.premiumArgs, mExecuteCommand);
    1413             : 
    1414           0 :         const bool misraC2023 = mSettings.premiumArgs.find("--misra-c-2023") != std::string::npos;
    1415             : 
    1416           0 :         for (const picojson::value& res : results) {
    1417             :             // TODO: get rid of copy?
    1418             :             // this is a copy so we can access missing fields and get a default value
    1419           0 :             picojson::object obj = res.get<picojson::object>();
    1420             : 
    1421           0 :             ErrorMessage errmsg;
    1422             : 
    1423           0 :             if (obj.count("file") > 0) {
    1424           0 :                 std::string fileName = obj["file"].get<std::string>();
    1425           0 :                 const int64_t lineNumber = obj["linenr"].get<int64_t>();
    1426           0 :                 const int64_t column = obj["column"].get<int64_t>();
    1427           0 :                 errmsg.callStack.emplace_back(std::move(fileName), lineNumber, column);
    1428           0 :             } else if (obj.count("loc") > 0) {
    1429           0 :                 for (const picojson::value &locvalue: obj["loc"].get<picojson::array>()) {
    1430           0 :                     picojson::object loc = locvalue.get<picojson::object>();
    1431           0 :                     std::string fileName = loc["file"].get<std::string>();
    1432           0 :                     const int64_t lineNumber = loc["linenr"].get<int64_t>();
    1433           0 :                     const int64_t column = loc["column"].get<int64_t>();
    1434           0 :                     std::string info = loc["info"].get<std::string>();
    1435           0 :                     errmsg.callStack.emplace_back(std::move(fileName), std::move(info), lineNumber, column);
    1436             :                 }
    1437             :             }
    1438             : 
    1439           0 :             errmsg.id = obj["addon"].get<std::string>() + "-" + obj["errorId"].get<std::string>();
    1440           0 :             if (misraC2023 && startsWith(errmsg.id, "misra-c2012-"))
    1441           0 :                 errmsg.id = "misra-c2023-" + errmsg.id.substr(12);
    1442           0 :             errmsg.setmsg(mSettings.getMisraRuleText(errmsg.id, obj["message"].get<std::string>()));
    1443           0 :             const std::string severity = obj["severity"].get<std::string>();
    1444           0 :             errmsg.severity = severityFromString(severity);
    1445           0 :             if (errmsg.severity == Severity::none || errmsg.severity == Severity::internal) {
    1446           0 :                 if (!endsWith(errmsg.id, "-logChecker"))
    1447           0 :                     continue;
    1448           0 :                 errmsg.severity = Severity::internal;
    1449             :             }
    1450           0 :             else if (!mSettings.severity.isEnabled(errmsg.severity)) {
    1451             :                 // Do not filter out premium misra/cert/autosar messages that has been
    1452             :                 // explicitly enabled with a --premium option
    1453           0 :                 if (!isPremiumCodingStandardId(errmsg.id))
    1454           0 :                     continue;
    1455             :             }
    1456           0 :             errmsg.file0 = file0;
    1457             : 
    1458           0 :             reportErr(errmsg);
    1459             :         }
    1460             :     }
    1461             : }
    1462             : 
    1463          25 : void CppCheck::executeAddonsWholeProgram(const std::list<FileWithDetails> &files)
    1464             : {
    1465          25 :     if (mSettings.addons.empty())
    1466          25 :         return;
    1467             : 
    1468           0 :     std::vector<std::string> ctuInfoFiles;
    1469           0 :     for (const auto &f: files) {
    1470           0 :         const std::string &dumpFileName = getDumpFileName(mSettings, f.path());
    1471           0 :         ctuInfoFiles.push_back(getCtuInfoFileName(dumpFileName));
    1472             :     }
    1473             : 
    1474             :     try {
    1475           0 :         executeAddons(ctuInfoFiles, "");
    1476           0 :     } catch (const InternalError& e) {
    1477           0 :         const ErrorMessage errmsg = ErrorMessage::fromInternalError(e, nullptr, "", "Bailing out from analysis: Whole program analysis failed");
    1478           0 :         reportErr(errmsg);
    1479             :     }
    1480             : 
    1481           0 :     if (mSettings.buildDir.empty()) {
    1482           0 :         for (const std::string &f: ctuInfoFiles)
    1483           0 :             std::remove(f.c_str());
    1484             :     }
    1485             : }
    1486             : 
    1487        2527 : Settings &CppCheck::settings()
    1488             : {
    1489        2527 :     return mSettings;
    1490             : }
    1491             : 
    1492          29 : void CppCheck::tooManyConfigsError(const std::string &file, const int numberOfConfigurations)
    1493             : {
    1494          29 :     if (!mSettings.severity.isEnabled(Severity::information) && !mTooManyConfigs)
    1495          25 :         return;
    1496             : 
    1497          29 :     mTooManyConfigs = false;
    1498             : 
    1499          29 :     if (mSettings.severity.isEnabled(Severity::information) && file.empty())
    1500          25 :         return;
    1501             : 
    1502           8 :     std::list<ErrorMessage::FileLocation> loclist;
    1503           4 :     if (!file.empty()) {
    1504           0 :         loclist.emplace_back(file, 0, 0);
    1505             :     }
    1506             : 
    1507           8 :     std::ostringstream msg;
    1508           4 :     msg << "Too many #ifdef configurations - cppcheck only checks " << mSettings.maxConfigs;
    1509           4 :     if (numberOfConfigurations > mSettings.maxConfigs)
    1510           0 :         msg << " of " << numberOfConfigurations << " configurations. Use --force to check all configurations.\n";
    1511           4 :     if (file.empty())
    1512           4 :         msg << " configurations. Use --force to check all configurations. For more details, use --enable=information.\n";
    1513             :     msg << "The checking of the file will be interrupted because there are too many "
    1514             :         "#ifdef configurations. Checking of all #ifdef configurations can be forced "
    1515             :         "by --force command line option or from GUI preferences. However that may "
    1516           4 :         "increase the checking time.";
    1517           4 :     if (file.empty())
    1518           4 :         msg << " For more details, use --enable=information.";
    1519             : 
    1520             : 
    1521           4 :     ErrorMessage errmsg(std::move(loclist),
    1522             :                         emptyString,
    1523             :                         Severity::information,
    1524           8 :                         msg.str(),
    1525             :                         "toomanyconfigs", CWE398,
    1526          24 :                         Certainty::normal);
    1527             : 
    1528           4 :     reportErr(errmsg);
    1529             : }
    1530             : 
    1531           9 : void CppCheck::purgedConfigurationMessage(const std::string &file, const std::string& configuration)
    1532             : {
    1533           9 :     mTooManyConfigs = false;
    1534             : 
    1535           9 :     if (mSettings.severity.isEnabled(Severity::information) && file.empty())
    1536           0 :         return;
    1537             : 
    1538          18 :     std::list<ErrorMessage::FileLocation> loclist;
    1539           9 :     if (!file.empty()) {
    1540           5 :         loclist.emplace_back(file, 0, 0);
    1541             :     }
    1542             : 
    1543           9 :     ErrorMessage errmsg(std::move(loclist),
    1544             :                         emptyString,
    1545             :                         Severity::information,
    1546          18 :                         "The configuration '" + configuration + "' was not checked because its code equals another one.",
    1547             :                         "purgedConfiguration",
    1548          54 :                         Certainty::normal);
    1549             : 
    1550           9 :     reportErr(errmsg);
    1551             : }
    1552             : 
    1553             : //---------------------------------------------------------------------------
    1554             : 
    1555             : // TODO: part of this logic is duplicated in Executor::hasToLog()
    1556      154951 : void CppCheck::reportErr(const ErrorMessage &msg)
    1557             : {
    1558      154951 :     if (msg.severity == Severity::internal) {
    1559      136567 :         mErrorLogger.reportErr(msg);
    1560      152862 :         return;
    1561             :     }
    1562             : 
    1563       18384 :     if (!mSettings.library.reportErrors(msg.file0))
    1564           0 :         return;
    1565             : 
    1566       18382 :     std::set<std::string> macroNames;
    1567       18382 :     if (!msg.callStack.empty()) {
    1568       36748 :         const std::string &file = msg.callStack.back().getfile(false);
    1569       18374 :         int lineNumber = msg.callStack.back().line;
    1570       18374 :         const auto it = mLocationMacros.find(Location(file, lineNumber));
    1571       18374 :         if (it != mLocationMacros.cend())
    1572         732 :             macroNames = it->second;
    1573             :     }
    1574             : 
    1575             :     // TODO: only convert if necessary
    1576       18381 :     const auto errorMessage = SuppressionList::ErrorMessage::fromErrorMessage(msg, macroNames);
    1577             : 
    1578       18382 :     if (mSettings.supprs.nomsg.isSuppressed(errorMessage, mUseGlobalSuppressions)) {
    1579             :         // Safety: Report critical errors to ErrorLogger
    1580       16284 :         if (mSettings.safety && ErrorLogger::isCriticalErrorId(msg.id)) {
    1581           0 :             mExitCode = 1;
    1582             : 
    1583           0 :             if (mSettings.supprs.nomsg.isSuppressedExplicitly(errorMessage, mUseGlobalSuppressions)) {
    1584             :                 // Report with internal severity to signal that there is this critical error but
    1585             :                 // it is suppressed
    1586           0 :                 ErrorMessage temp(msg);
    1587           0 :                 temp.severity = Severity::internal;
    1588           0 :                 mErrorLogger.reportErr(temp);
    1589             :             } else {
    1590             :                 // Report critical error that is not explicitly suppressed
    1591           0 :                 mErrorLogger.reportErr(msg);
    1592             :             }
    1593             :         }
    1594       16284 :         return;
    1595             :     }
    1596             : 
    1597             :     // TODO: there should be no need for the verbose and default messages here
    1598        2098 :     std::string errmsg = msg.toString(mSettings.verbose);
    1599        2098 :     if (errmsg.empty())
    1600           0 :         return;
    1601             : 
    1602             :     // Alert only about unique errors.
    1603             :     // This makes sure the errors of a single check() call are unique.
    1604             :     // TODO: get rid of this? This is forwarded to another ErrorLogger which is also doing this
    1605        2098 :     if (!mErrorList.emplace(std::move(errmsg)).second)
    1606           0 :         return;
    1607             : 
    1608        2098 :     if (!mSettings.buildDir.empty())
    1609           0 :         mAnalyzerInformation.reportErr(msg);
    1610             : 
    1611        2098 :     if (!mSettings.supprs.nofail.isSuppressed(errorMessage) && !mSettings.supprs.nomsg.isSuppressed(errorMessage)) {
    1612        2088 :         mExitCode = 1;
    1613             :     }
    1614             : 
    1615        2097 :     mErrorLogger.reportErr(msg);
    1616             :     // check if plistOutput should be populated and the current output file is open and the error is not suppressed
    1617        2098 :     if (!mSettings.plistOutput.empty() && mPlistFile.is_open() && !mSettings.supprs.nomsg.isSuppressed(errorMessage)) {
    1618             :         // add error to plist output file
    1619         600 :         mPlistFile << ErrorLogger::plistData(msg);
    1620             :     }
    1621             : }
    1622             : 
    1623           0 : void CppCheck::reportOut(const std::string &outmsg, Color c)
    1624             : {
    1625           0 :     mErrorLogger.reportOut(outmsg, c);
    1626           0 : }
    1627             : 
    1628      819043 : void CppCheck::reportProgress(const std::string &filename, const char stage[], const std::size_t value)
    1629             : {
    1630      819043 :     mErrorLogger.reportProgress(filename, stage, value);
    1631      819042 : }
    1632             : 
    1633           4 : void CppCheck::getErrorMessages(ErrorLogger &errorlogger)
    1634             : {
    1635           8 :     Settings s;
    1636           4 :     s.severity.enable(Severity::warning);
    1637           4 :     s.severity.enable(Severity::style);
    1638           4 :     s.severity.enable(Severity::portability);
    1639           4 :     s.severity.enable(Severity::performance);
    1640           4 :     s.severity.enable(Severity::information);
    1641             : 
    1642           8 :     CppCheck cppcheck(errorlogger, true, nullptr);
    1643           4 :     cppcheck.purgedConfigurationMessage(emptyString,emptyString);
    1644           4 :     cppcheck.mTooManyConfigs = true;
    1645           4 :     cppcheck.tooManyConfigsError(emptyString,0U);
    1646             :     // TODO: add functions to get remaining error messages
    1647             : 
    1648             :     // call all "getErrorMessages" in all registered Check classes
    1649         108 :     for (std::list<Check *>::const_iterator it = Check::instances().cbegin(); it != Check::instances().cend(); ++it)
    1650         104 :         (*it)->getErrorMessages(&errorlogger, &s);
    1651             : 
    1652           4 :     CheckUnusedFunctions::getErrorMessages(errorlogger);
    1653           4 :     Preprocessor::getErrorMessages(errorlogger, s);
    1654           4 : }
    1655             : 
    1656           2 : void CppCheck::analyseClangTidy(const FileSettings &fileSettings)
    1657             : {
    1658           2 :     std::string allIncludes;
    1659           2 :     for (const std::string &inc : fileSettings.includePaths) {
    1660           0 :         allIncludes = allIncludes + "-I\"" + inc + "\" ";
    1661             :     }
    1662             : 
    1663           2 :     const std::string allDefines = getDefinesFlags(fileSettings.defines);
    1664             : 
    1665             : #ifdef _WIN32
    1666             :     constexpr char exe[] = "clang-tidy.exe";
    1667             : #else
    1668           2 :     constexpr char exe[] = "clang-tidy";
    1669             : #endif
    1670             : 
    1671           4 :     const std::string args = "-quiet -checks=*,-clang-analyzer-*,-llvm* \"" + fileSettings.filename() + "\" -- " + allIncludes + allDefines;
    1672           2 :     std::string output;
    1673           2 :     if (const int exitcode = mExecuteCommand(exe, split(args), emptyString, output)) {
    1674           0 :         std::cerr << "Failed to execute '" << exe << "' (exitcode: " << std::to_string(exitcode) << ")" << std::endl;
    1675           0 :         return;
    1676             :     }
    1677             : 
    1678             :     // parse output and create error messages
    1679           4 :     std::istringstream istr(output);
    1680           4 :     std::string line;
    1681             : 
    1682           2 :     if (!mSettings.buildDir.empty()) {
    1683           0 :         const std::string analyzerInfoFile = AnalyzerInformation::getAnalyzerInfoFile(mSettings.buildDir, fileSettings.filename(), emptyString);
    1684           0 :         std::ofstream fcmd(analyzerInfoFile + ".clang-tidy-cmd");
    1685           0 :         fcmd << istr.str();
    1686             :     }
    1687             : 
    1688           2 :     while (std::getline(istr, line)) {
    1689           0 :         if (line.find("error") == std::string::npos && line.find("warning") == std::string::npos)
    1690           0 :             continue;
    1691             : 
    1692           0 :         std::size_t endColumnPos = line.find(": error:");
    1693           0 :         if (endColumnPos == std::string::npos) {
    1694           0 :             endColumnPos = line.find(": warning:");
    1695             :         }
    1696             : 
    1697           0 :         const std::size_t endLinePos = line.rfind(':', endColumnPos-1);
    1698           0 :         const std::size_t endNamePos = line.rfind(':', endLinePos - 1);
    1699           0 :         const std::size_t endMsgTypePos = line.find(':', endColumnPos + 2);
    1700           0 :         const std::size_t endErrorPos = line.rfind('[', std::string::npos);
    1701           0 :         if (endLinePos==std::string::npos || endNamePos==std::string::npos || endMsgTypePos==std::string::npos || endErrorPos==std::string::npos)
    1702           0 :             continue;
    1703             : 
    1704           0 :         const std::string lineNumString = line.substr(endNamePos + 1, endLinePos - endNamePos - 1);
    1705           0 :         const std::string columnNumString = line.substr(endLinePos + 1, endColumnPos - endLinePos - 1);
    1706           0 :         const std::string messageString = line.substr(endMsgTypePos + 1, endErrorPos - endMsgTypePos - 1);
    1707           0 :         const std::string errorString = line.substr(endErrorPos, line.length());
    1708             : 
    1709           0 :         std::string fixedpath = Path::simplifyPath(line.substr(0, endNamePos));
    1710           0 :         const int64_t lineNumber = strToInt<int64_t>(lineNumString);
    1711           0 :         const int64_t column = strToInt<int64_t>(columnNumString);
    1712           0 :         fixedpath = Path::toNativeSeparators(std::move(fixedpath));
    1713             : 
    1714           0 :         ErrorMessage errmsg;
    1715           0 :         errmsg.callStack.emplace_back(fixedpath, lineNumber, column);
    1716             : 
    1717           0 :         errmsg.id = "clang-tidy-" + errorString.substr(1, errorString.length() - 2);
    1718           0 :         if (errmsg.id.find("performance") != std::string::npos)
    1719           0 :             errmsg.severity = Severity::performance;
    1720           0 :         else if (errmsg.id.find("portability") != std::string::npos)
    1721           0 :             errmsg.severity = Severity::portability;
    1722           0 :         else if (errmsg.id.find("cert") != std::string::npos || errmsg.id.find("misc") != std::string::npos || errmsg.id.find("unused") != std::string::npos)
    1723           0 :             errmsg.severity = Severity::warning;
    1724             :         else
    1725           0 :             errmsg.severity = Severity::style;
    1726             : 
    1727           0 :         errmsg.file0 = std::move(fixedpath);
    1728           0 :         errmsg.setmsg(messageString);
    1729           0 :         reportErr(errmsg);
    1730             :     }
    1731             : }
    1732             : 
    1733         126 : bool CppCheck::analyseWholeProgram()
    1734             : {
    1735         126 :     bool errors = false;
    1736             :     // Init CTU
    1737         126 :     CTU::maxCtuDepth = mSettings.maxCtuDepth;
    1738             :     // Analyse the tokens
    1739         126 :     CTU::FileInfo ctu;
    1740         638 :     for (const Check::FileInfo *fi : mFileInfo) {
    1741         512 :         const auto *fi2 = dynamic_cast<const CTU::FileInfo *>(fi);
    1742         512 :         if (fi2) {
    1743         440 :             ctu.functionCalls.insert(ctu.functionCalls.end(), fi2->functionCalls.cbegin(), fi2->functionCalls.cend());
    1744         440 :             ctu.nestedCalls.insert(ctu.nestedCalls.end(), fi2->nestedCalls.cbegin(), fi2->nestedCalls.cend());
    1745             :         }
    1746             :     }
    1747             : 
    1748             :     // cppcheck-suppress shadowFunction - TODO: fix this
    1749        3402 :     for (Check *check : Check::instances())
    1750        3276 :         errors |= check->analyseWholeProgram(&ctu, mFileInfo, mSettings, *this);  // TODO: ctu
    1751             : 
    1752         126 :     if (mUnusedFunctionsCheck)
    1753           2 :         errors |= mUnusedFunctionsCheck->check(mSettings, *this);
    1754             : 
    1755         252 :     return errors && (mExitCode > 0);
    1756             : }
    1757             : 
    1758          25 : unsigned int CppCheck::analyseWholeProgram(const std::string &buildDir, const std::list<FileWithDetails> &files, const std::list<FileSettings>& fileSettings)
    1759             : {
    1760          25 :     executeAddonsWholeProgram(files); // TODO: pass FileSettings
    1761          25 :     if (buildDir.empty()) {
    1762          25 :         removeCtuInfoFiles(files, fileSettings);
    1763          25 :         return mExitCode;
    1764             :     }
    1765           0 :     if (mSettings.checks.isEnabled(Checks::unusedFunction))
    1766           0 :         CheckUnusedFunctions::analyseWholeProgram(mSettings, *this, buildDir);
    1767           0 :     std::list<Check::FileInfo*> fileInfoList;
    1768           0 :     CTU::FileInfo ctuFileInfo;
    1769             : 
    1770             :     // Load all analyzer info data..
    1771           0 :     const std::string filesTxt(buildDir + "/files.txt");
    1772           0 :     std::ifstream fin(filesTxt);
    1773           0 :     std::string filesTxtLine;
    1774           0 :     while (std::getline(fin, filesTxtLine)) {
    1775           0 :         const std::string::size_type firstColon = filesTxtLine.find(':');
    1776           0 :         if (firstColon == std::string::npos)
    1777           0 :             continue;
    1778           0 :         const std::string::size_type lastColon = filesTxtLine.rfind(':');
    1779           0 :         if (firstColon == lastColon)
    1780           0 :             continue;
    1781           0 :         const std::string xmlfile = buildDir + '/' + filesTxtLine.substr(0,firstColon);
    1782             :         //const std::string sourcefile = filesTxtLine.substr(lastColon+1);
    1783             : 
    1784           0 :         tinyxml2::XMLDocument doc;
    1785           0 :         const tinyxml2::XMLError error = doc.LoadFile(xmlfile.c_str());
    1786           0 :         if (error != tinyxml2::XML_SUCCESS)
    1787           0 :             continue;
    1788             : 
    1789           0 :         const tinyxml2::XMLElement * const rootNode = doc.FirstChildElement();
    1790           0 :         if (rootNode == nullptr)
    1791           0 :             continue;
    1792             : 
    1793           0 :         for (const tinyxml2::XMLElement *e = rootNode->FirstChildElement(); e; e = e->NextSiblingElement()) {
    1794           0 :             if (std::strcmp(e->Name(), "FileInfo") != 0)
    1795           0 :                 continue;
    1796           0 :             const char *checkClassAttr = e->Attribute("check");
    1797           0 :             if (!checkClassAttr)
    1798           0 :                 continue;
    1799           0 :             if (std::strcmp(checkClassAttr, "ctu") == 0) {
    1800           0 :                 ctuFileInfo.loadFromXml(e);
    1801           0 :                 continue;
    1802             :             }
    1803             :             // cppcheck-suppress shadowFunction - TODO: fix this
    1804           0 :             for (const Check *check : Check::instances()) {
    1805           0 :                 if (checkClassAttr == check->name())
    1806           0 :                     fileInfoList.push_back(check->loadFileInfoFromXml(e));
    1807             :             }
    1808             :         }
    1809             :     }
    1810             : 
    1811             :     // Set CTU max depth
    1812           0 :     CTU::maxCtuDepth = mSettings.maxCtuDepth;
    1813             : 
    1814             :     // Analyse the tokens
    1815             :     // cppcheck-suppress shadowFunction - TODO: fix this
    1816           0 :     for (Check *check : Check::instances())
    1817           0 :         check->analyseWholeProgram(&ctuFileInfo, fileInfoList, mSettings, *this);
    1818             : 
    1819           0 :     if (mUnusedFunctionsCheck)
    1820           0 :         mUnusedFunctionsCheck->check(mSettings, *this);
    1821             : 
    1822           0 :     for (Check::FileInfo *fi : fileInfoList)
    1823           0 :         delete fi;
    1824             : 
    1825           0 :     return mExitCode;
    1826             : }
    1827             : 
    1828          25 : void CppCheck::removeCtuInfoFiles(const std::list<FileWithDetails> &files, const std::list<FileSettings>& fileSettings)
    1829             : {
    1830          25 :     if (mSettings.buildDir.empty()) {
    1831          50 :         for (const auto& f: files) {
    1832          50 :             const std::string &dumpFileName = getDumpFileName(mSettings, f.path());
    1833          50 :             const std::string &ctuInfoFileName = getCtuInfoFileName(dumpFileName);
    1834          25 :             std::remove(ctuInfoFileName.c_str());
    1835             :         }
    1836          25 :         for (const auto& fs: fileSettings) {
    1837           0 :             const std::string &dumpFileName = getDumpFileName(mSettings, fs.filename());
    1838           0 :             const std::string &ctuInfoFileName = getCtuInfoFileName(dumpFileName);
    1839           0 :             std::remove(ctuInfoFileName.c_str());
    1840             :         }
    1841             :     }
    1842          25 : }
    1843             : 
    1844             : // cppcheck-suppress unusedFunction - only used in tests
    1845        4701 : void CppCheck::resetTimerResults()
    1846             : {
    1847        4701 :     s_timerResults.reset();
    1848        4701 : }
    1849             : 
    1850          42 : void CppCheck::printTimerResults(SHOWTIME_MODES mode)
    1851             : {
    1852          42 :     s_timerResults.showResults(mode);
    1853          42 : }
    1854             : 
    1855          16 : bool CppCheck::isPremiumCodingStandardId(const std::string& id) const {
    1856          16 :     if (mSettings.premiumArgs.find("--misra") != std::string::npos) {
    1857           8 :         if (startsWith(id, "misra-") || startsWith(id, "premium-misra-"))
    1858           6 :             return true;
    1859             :     }
    1860          10 :     if (mSettings.premiumArgs.find("--cert") != std::string::npos && startsWith(id, "premium-cert-"))
    1861           1 :         return true;
    1862           9 :     if (mSettings.premiumArgs.find("--autosar") != std::string::npos && startsWith(id, "premium-autosar-"))
    1863           1 :         return true;
    1864           8 :     return false;
    1865             : }
    1866             : 
    1867           1 : std::string CppCheck::getDumpFileContentsRawTokens(const std::vector<std::string>& files, const simplecpp::TokenList& tokens1) const {
    1868           1 :     std::string dumpProlog;
    1869           1 :     dumpProlog += "  <rawtokens>\n";
    1870           2 :     for (unsigned int i = 0; i < files.size(); ++i) {
    1871           1 :         dumpProlog += "    <file index=\"";
    1872           1 :         dumpProlog += std::to_string(i);
    1873           1 :         dumpProlog += "\" name=\"";
    1874           1 :         dumpProlog += ErrorLogger::toxml(Path::getRelativePath(files[i], mSettings.basePaths));
    1875           1 :         dumpProlog += "\"/>\n";
    1876             :     }
    1877           1 :     for (const simplecpp::Token *tok = tokens1.cfront(); tok; tok = tok->next) {
    1878           0 :         dumpProlog += "    <tok ";
    1879             : 
    1880           0 :         dumpProlog += "fileIndex=\"";
    1881           0 :         dumpProlog += std::to_string(tok->location.fileIndex);
    1882           0 :         dumpProlog += "\" ";
    1883             : 
    1884           0 :         dumpProlog += "linenr=\"";
    1885           0 :         dumpProlog += std::to_string(tok->location.line);
    1886           0 :         dumpProlog += "\" ";
    1887             : 
    1888           0 :         dumpProlog +="column=\"";
    1889           0 :         dumpProlog += std::to_string(tok->location.col);
    1890           0 :         dumpProlog += "\" ";
    1891             : 
    1892           0 :         dumpProlog += "str=\"";
    1893           0 :         dumpProlog += ErrorLogger::toxml(tok->str());
    1894           0 :         dumpProlog += "\"";
    1895             : 
    1896           0 :         dumpProlog += "/>\n";
    1897             :     }
    1898           1 :     dumpProlog += "  </rawtokens>\n";
    1899           1 :     return dumpProlog;
    1900             : }

Generated by: LCOV version 1.14