Cppcheck
cppcheck.cpp
Go to the documentation of this file.
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 
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  FilesDeleter() = default;
93  ~FilesDeleter() {
94  for (const std::string& fileName: mFilenames)
95  std::remove(fileName.c_str());
96  }
97  void addFile(const std::string& fileName) {
98  mFilenames.push_back(fileName);
99  }
100  private:
101  std::vector<std::string> mFilenames;
102  };
103 }
104 
105 static std::string cmdFileName(std::string f)
106 {
107  f = Path::toNativeSeparators(std::move(f));
108  if (f.find(' ') != std::string::npos)
109  return "\"" + f + "\"";
110  return f;
111 }
112 
113 static std::vector<std::string> split(const std::string &str, const std::string &sep=" ")
114 {
115  std::vector<std::string> ret;
116  for (std::string::size_type startPos = 0U; startPos < str.size();) {
117  startPos = str.find_first_not_of(sep, startPos);
118  if (startPos == std::string::npos)
119  break;
120 
121  if (str[startPos] == '\"') {
122  const std::string::size_type endPos = str.find('\"', startPos + 1);
123  ret.push_back(str.substr(startPos + 1, endPos - startPos - 1));
124  startPos = (endPos < str.size()) ? (endPos + 1) : endPos;
125  continue;
126  }
127 
128  const std::string::size_type endPos = str.find(sep, startPos + 1);
129  ret.push_back(str.substr(startPos, endPos - startPos));
130  startPos = endPos;
131  }
132 
133  return ret;
134 }
135 
136 static std::string getDumpFileName(const Settings& settings, const std::string& filename)
137 {
138  if (!settings.dumpFile.empty())
139  return settings.dumpFile;
140 
141  std::string extension;
142  if (settings.dump)
143  extension = ".dump";
144  else
145  extension = "." + std::to_string(settings.pid) + ".dump";
146 
147  if (!settings.dump && !settings.buildDir.empty())
148  return AnalyzerInformation::getAnalyzerInfoFile(settings.buildDir, filename, emptyString) + extension;
149  return filename + extension;
150 }
151 
152 static std::string getCtuInfoFileName(const std::string &dumpFile)
153 {
154  return dumpFile.substr(0, dumpFile.size()-4) + "ctu-info";
155 }
156 
157 static void createDumpFile(const Settings& settings,
158  const std::string& filename,
159  std::ofstream& fdump,
160  std::string& dumpFile)
161 {
162  if (!settings.dump && settings.addons.empty())
163  return;
164  dumpFile = getDumpFileName(settings, filename);
165 
166  fdump.open(dumpFile);
167  if (!fdump.is_open())
168  return;
169 
170  {
171  std::ofstream fout(getCtuInfoFileName(dumpFile));
172  }
173 
174  std::string language;
175  switch (settings.enforcedLang) {
176  case Standards::Language::C:
177  language = " language=\"c\"";
178  break;
179  case Standards::Language::CPP:
180  language = " language=\"cpp\"";
181  break;
182  case Standards::Language::None:
183  {
184  // TODO: error out on unknown language?
185  const Standards::Language lang = Path::identify(filename);
186  if (lang == Standards::Language::CPP)
187  language = " language=\"cpp\"";
188  else if (lang == Standards::Language::C)
189  language = " language=\"c\"";
190  break;
191  }
192  }
193 
194  fdump << "<?xml version=\"1.0\"?>\n";
195  fdump << "<dumps" << language << ">\n";
196  fdump << " <platform"
197  << " name=\"" << settings.platform.toString() << '\"'
198  << " char_bit=\"" << settings.platform.char_bit << '\"'
199  << " short_bit=\"" << settings.platform.short_bit << '\"'
200  << " int_bit=\"" << settings.platform.int_bit << '\"'
201  << " long_bit=\"" << settings.platform.long_bit << '\"'
202  << " long_long_bit=\"" << settings.platform.long_long_bit << '\"'
203  << " pointer_bit=\"" << (settings.platform.sizeof_pointer * settings.platform.char_bit) << '\"'
204  << "/>" << '\n';
205 }
206 
207 static std::string detectPython(const CppCheck::ExecuteCmdFn &executeCommand)
208 {
209 #ifdef _WIN32
210  const char *py_exes[] = { "python3.exe", "python.exe" };
211 #else
212  const char *py_exes[] = { "python3", "python" };
213 #endif
214  for (const char* py_exe : py_exes) {
215  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  if (executeCommand(py_exe, split("--version"), "2>&1", out) == EXIT_SUCCESS && startsWith(out, "Python ") && std::isdigit(out[7])) {
225  return py_exe;
226  }
227  }
228  return "";
229 }
230 
231 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  const std::string redirect = "2>&1";
238 
239  std::string pythonExe;
240 
241  if (!addonInfo.executable.empty())
242  pythonExe = addonInfo.executable;
243  else if (!addonInfo.python.empty())
244  pythonExe = cmdFileName(addonInfo.python);
245  else if (!defaultPythonExe.empty())
246  pythonExe = cmdFileName(defaultPythonExe);
247  else {
248  // store in static variable so we only look this up once
249  static const std::string detectedPythonExe = detectPython(executeCommand);
250  if (detectedPythonExe.empty())
251  throw InternalError(nullptr, "Failed to auto detect python");
252  pythonExe = detectedPythonExe;
253  }
254 
255  std::string args;
256  if (addonInfo.executable.empty())
257  args = cmdFileName(addonInfo.runScript) + " " + cmdFileName(addonInfo.scriptFile);
258  args += std::string(args.empty() ? "" : " ") + "--cli" + addonInfo.args;
259  if (!premiumArgs.empty() && !addonInfo.executable.empty())
260  args += " " + premiumArgs;
261 
262  const bool is_file_list = (file.find(FILELIST) != std::string::npos);
263  const std::string fileArg = (is_file_list ? " --file-list " : " ") + cmdFileName(file);
264  args += fileArg;
265 
266  std::string result;
267  if (const int exitcode = executeCommand(pythonExe, split(args), redirect, result)) {
268  std::string message("Failed to execute addon '" + addonInfo.name + "' - exitcode is " + std::to_string(exitcode));
269  std::string details = pythonExe + " " + args;
270  if (result.size() > 2) {
271  details += "\nOutput:\n";
272  details += result;
273  const auto pos = details.find_last_not_of("\n\r");
274  if (pos != std::string::npos)
275  details.resize(pos + 1);
276  }
277  throw InternalError(nullptr, std::move(message), std::move(details));
278  }
279 
280  std::vector<picojson::value> addonResult;
281 
282  // Validate output..
283  std::istringstream istr(result);
284  std::string line;
285  while (std::getline(istr, line)) {
286  // TODO: also bail out?
287  if (line.empty()) {
288  //std::cout << "addon '" << addonInfo.name << "' result contains empty line" << std::endl;
289  continue;
290  }
291 
292  // TODO: get rid of this
293  if (startsWith(line,"Checking ")) {
294  //std::cout << "addon '" << addonInfo.name << "' result contains 'Checking ' line" << std::endl;
295  continue;
296  }
297 
298  if (line[0] != '{') {
299  //std::cout << "addon '" << addonInfo.name << "' result is not a JSON" << std::endl;
300 
301  result.erase(result.find_last_not_of('\n') + 1, std::string::npos); // Remove trailing newlines
302  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  picojson::value res;
309  const std::string err = picojson::parse(res, line);
310  if (!err.empty()) {
311  //std::cout << "addon '" << addonInfo.name << "' result is not a valid JSON (" << err << ")" << std::endl;
312  continue;
313  }
314  if (!res.is<picojson::object>()) {
315  //std::cout << "addon '" << addonInfo.name << "' result is not a JSON object" << std::endl;
316  continue;
317  }
318  addonResult.emplace_back(std::move(res));
319  }
320 
321  // Valid results
322  return addonResult;
323 }
324 
325 static std::string getDefinesFlags(const std::string &semicolonSeparatedString)
326 {
327  std::string flags;
328  for (const std::string &d: split(semicolonSeparatedString, ";"))
329  flags += "-D" + d + " ";
330  return flags;
331 }
332 
334  bool useGlobalSuppressions,
335  ExecuteCmdFn executeCommand)
336  : mErrorLogger(errorLogger)
337  , mUseGlobalSuppressions(useGlobalSuppressions)
338  , mExecuteCommand(std::move(executeCommand))
339 {}
340 
342 {
343  while (!mFileInfo.empty()) {
344  delete mFileInfo.back();
345  mFileInfo.pop_back();
346  }
347 
348  if (mPlistFile.is_open()) {
350  mPlistFile.close();
351  }
352 }
353 
354 const char * CppCheck::version()
355 {
356  return Version;
357 }
358 
360 {
361  return ExtraVersion;
362 }
363 
364 static bool reportClangErrors(std::istream &is, const std::function<void(const ErrorMessage&)>& reportErr, std::vector<ErrorMessage> &warnings)
365 {
366  std::string line;
367  while (std::getline(is, line)) {
368  if (line.empty() || line[0] == ' ' || line[0] == '`' || line[0] == '-')
369  continue;
370 
371  std::string::size_type pos3 = line.find(": error: ");
372  if (pos3 == std::string::npos)
373  pos3 = line.find(": fatal error:");
374  if (pos3 == std::string::npos)
375  pos3 = line.find(": warning:");
376  if (pos3 == std::string::npos)
377  continue;
378 
379  // file:line:column: error: ....
380  const std::string::size_type pos2 = line.rfind(':', pos3 - 1);
381  const std::string::size_type pos1 = line.rfind(':', pos2 - 1);
382 
383  if (pos1 >= pos2 || pos2 >= pos3)
384  continue;
385 
386  const std::string filename = line.substr(0, pos1);
387  const std::string linenr = line.substr(pos1+1, pos2-pos1-1);
388  const std::string colnr = line.substr(pos2+1, pos3-pos2-1);
389  const std::string msg = line.substr(line.find(':', pos3+1) + 2);
390 
391  const std::string locFile = Path::toNativeSeparators(filename);
392  const int line_i = strToInt<int>(linenr);
393  const int column = strToInt<unsigned int>(colnr);
394  ErrorMessage::FileLocation loc(locFile, line_i, column);
395  ErrorMessage errmsg({std::move(loc)},
396  locFile,
398  msg,
399  "syntaxError",
401 
402  if (line.compare(pos3, 10, ": warning:") == 0) {
403  warnings.push_back(std::move(errmsg));
404  continue;
405  }
406 
407  reportErr(errmsg);
408 
409  return true;
410  }
411  return false;
412 }
413 
414 unsigned int CppCheck::checkClang(const std::string &path)
415 {
418 
419  if (!mSettings.quiet)
420  mErrorLogger.reportOut(std::string("Checking ") + path + " ...", Color::FgGreen);
421 
422  // TODO: this ignores the configured language
423  const bool isCpp = Path::identify(path) == Standards::Language::CPP;
424  const std::string langOpt = isCpp ? "-x c++" : "-x c";
425  const std::string analyzerInfo = mSettings.buildDir.empty() ? std::string() : AnalyzerInformation::getAnalyzerInfoFile(mSettings.buildDir, path, emptyString);
426  const std::string clangcmd = analyzerInfo + ".clang-cmd";
427  const std::string clangStderr = analyzerInfo + ".clang-stderr";
428  const std::string clangAst = analyzerInfo + ".clang-ast";
429  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  std::string flags(langOpt + " ");
438  // TODO: does not apply C standard
439  if (isCpp && !mSettings.standards.stdValue.empty())
440  flags += "-std=" + mSettings.standards.stdValue + " ";
441 
442  for (const std::string &i: mSettings.includePaths)
443  flags += "-I" + i + " ";
444 
446 
447  const std::string args2 = "-fsyntax-only -Xclang -ast-dump -fno-color-diagnostics " + flags + path;
448  const std::string redirect2 = analyzerInfo.empty() ? std::string("2>&1") : ("2> " + clangStderr);
449  if (!mSettings.buildDir.empty()) {
450  std::ofstream fout(clangcmd);
451  fout << exe << " " << args2 << " " << redirect2 << std::endl;
452  } else if (mSettings.verbose && !mSettings.quiet) {
453  mErrorLogger.reportOut(exe + " " + args2);
454  }
455 
456  std::string output2;
457  const int exitcode = mExecuteCommand(exe,split(args2),redirect2,output2);
458  if (exitcode != EXIT_SUCCESS) {
459  // TODO: report as proper error
460  std::cerr << "Failed to execute '" << exe << " " << args2 << " " << redirect2 << "' - (exitcode: " << exitcode << " / output: " << output2 << ")" << std::endl;
461  return 0; // TODO: report as failure?
462  }
463 
464  if (output2.find("TranslationUnitDecl") == std::string::npos) {
465  // TODO: report as proper error
466  std::cerr << "Failed to execute '" << exe << " " << args2 << " " << redirect2 << "' - (no TranslationUnitDecl in output)" << std::endl;
467  return 0; // TODO: report as failure?
468  }
469 
470  // Ensure there are not syntax errors...
471  std::vector<ErrorMessage> compilerWarnings;
472  if (!mSettings.buildDir.empty()) {
473  std::ifstream fin(clangStderr);
474  auto reportError = [this](const ErrorMessage& errorMessage) {
475  reportErr(errorMessage);
476  };
477  if (reportClangErrors(fin, reportError, compilerWarnings))
478  return 0;
479  } else {
480  std::istringstream istr(output2);
481  auto reportError = [this](const ErrorMessage& errorMessage) {
482  reportErr(errorMessage);
483  };
484  if (reportClangErrors(istr, reportError, compilerWarnings))
485  return 0;
486  }
487 
488  if (!mSettings.buildDir.empty()) {
489  std::ofstream fout(clangAst);
490  fout << output2 << std::endl;
491  }
492 
493  try {
494  Tokenizer tokenizer(mSettings, *this);
495  tokenizer.list.appendFileIfNew(path);
496  std::istringstream ast(output2);
497  clangimport::parseClangAstDump(tokenizer, ast);
498  ValueFlow::setValues(tokenizer.list,
499  const_cast<SymbolDatabase&>(*tokenizer.getSymbolDatabase()),
500  *this,
501  mSettings,
502  &s_timerResults);
504  tokenizer.printDebugOutput(1);
505  checkNormalTokens(tokenizer);
506 
507  // create dumpfile
508  std::ofstream fdump;
509  std::string dumpFile;
510  createDumpFile(mSettings, path, fdump, dumpFile);
511  if (fdump.is_open()) {
512  // TODO: use tinyxml2 to create XML
513  fdump << "<dump cfg=\"\">\n";
514  for (const ErrorMessage& errmsg: compilerWarnings)
515  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  fdump << " <standards>\n";
517  fdump << " <c version=\"" << mSettings.standards.getC() << "\"/>\n";
518  fdump << " <cpp version=\"" << mSettings.standards.getCPP() << "\"/>\n";
519  fdump << " </standards>\n";
520  tokenizer.dump(fdump);
521  fdump << "</dump>\n";
522  fdump << "</dumps>\n";
523  fdump.close();
524  }
525 
526  // run addons
527  executeAddons(dumpFile, path);
528 
529  } catch (const InternalError &e) {
530  const ErrorMessage errmsg = ErrorMessage::fromInternalError(e, nullptr, path, "Bailing out from analysis: Processing Clang AST dump failed");
531  reportErr(errmsg);
532  } catch (const TerminateException &) {
533  // Analysis is terminated
534  return mExitCode;
535  } catch (const std::exception &e) {
536  internalError(path, std::string("Processing Clang AST dump failed: ") + e.what());
537  }
538 
539  return mExitCode;
540 }
541 
542 unsigned int CppCheck::check(const std::string &path)
543 {
544  if (mSettings.clang)
545  return checkClang(Path::simplifyPath(path));
546 
548 }
549 
550 unsigned int CppCheck::check(const std::string &path, const std::string &content)
551 {
552  std::istringstream iss(content);
553  return checkFile(Path::simplifyPath(path), emptyString, &iss);
554 }
555 
556 unsigned int CppCheck::check(const FileSettings &fs)
557 {
558  // TODO: move to constructor when CppCheck no longer owns the settings
561 
563  temp.mSettings = mSettings;
564  if (!temp.mSettings.userDefines.empty())
565  temp.mSettings.userDefines += ';';
566  if (mSettings.clang)
567  temp.mSettings.userDefines += fs.defines;
568  else
571  temp.mSettings.userUndefs.insert(fs.undefs.cbegin(), fs.undefs.cend());
572  if (fs.standard.find("++") != std::string::npos)
574  else if (!fs.standard.empty())
575  temp.mSettings.standards.setC(fs.standard);
576  if (fs.platformType != Platform::Type::Unspecified)
578  if (mSettings.clang) {
579  temp.mSettings.includePaths.insert(temp.mSettings.includePaths.end(), fs.systemIncludePaths.cbegin(), fs.systemIncludePaths.cend());
580  // TODO: propagate back suppressions
581  const unsigned int returnValue = temp.check(Path::simplifyPath(fs.filename()));
583  mUnusedFunctionsCheck->updateFunctionData(*temp.mUnusedFunctionsCheck);
584  return returnValue;
585  }
586  const unsigned int returnValue = temp.checkFile(Path::simplifyPath(fs.filename()), fs.cfg);
589  mUnusedFunctionsCheck->updateFunctionData(*temp.mUnusedFunctionsCheck);
590  return returnValue;
591 }
592 
593 static simplecpp::TokenList createTokenList(const std::string& filename, std::vector<std::string>& files, simplecpp::OutputList* outputList, std::istream* fileStream)
594 {
595  if (fileStream)
596  return {*fileStream, files, filename, outputList};
597 
598  return {filename, files, outputList};
599 }
600 
601 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
606 
607  mExitCode = 0;
608 
609  if (Settings::terminated())
610  return mExitCode;
611 
612  const Timer fileTotalTimer(mSettings.showtime == SHOWTIME_MODES::SHOWTIME_FILE_TOTAL, filename);
613 
614  if (!mSettings.quiet) {
615  std::string fixedpath = Path::simplifyPath(filename);
616  fixedpath = Path::toNativeSeparators(std::move(fixedpath));
617  mErrorLogger.reportOut(std::string("Checking ") + fixedpath + ' ' + cfgname + std::string("..."), Color::FgGreen);
618 
619  if (mSettings.verbose) {
621  std::string undefs;
622  for (const std::string& U : mSettings.userUndefs) {
623  if (!undefs.empty())
624  undefs += ';';
625  undefs += ' ' + U;
626  }
627  mErrorLogger.reportOut("Undefines:" + undefs);
628  std::string includePaths;
629  for (const std::string &I : mSettings.includePaths)
630  includePaths += " -I" + I;
631  mErrorLogger.reportOut("Includes:" + includePaths);
632  mErrorLogger.reportOut(std::string("Platform:") + mSettings.platform.toString());
633  }
634  }
635 
636  if (mPlistFile.is_open()) {
638  mPlistFile.close();
639  }
640 
641  try {
642  if (mSettings.library.markupFile(filename)) {
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  Tokenizer tokenizer(mSettings, *this);
646  tokenizer.list.setLang(Standards::Language::C);
647  if (fileStream) {
648  tokenizer.list.createTokens(*fileStream, filename);
649  }
650  else {
651  std::ifstream in(filename);
652  tokenizer.list.createTokens(in, filename);
653  }
654  mUnusedFunctionsCheck->parseTokens(tokenizer, mSettings);
655  }
656  return EXIT_SUCCESS;
657  }
658 
659  simplecpp::OutputList outputList;
660  std::vector<std::string> files;
661  simplecpp::TokenList tokens1 = createTokenList(filename, files, &outputList, fileStream);
662 
663  // If there is a syntax error, report it and stop
664  const auto output_it = std::find_if(outputList.cbegin(), outputList.cend(), [](const simplecpp::Output &output){
665  return Preprocessor::hasErrors(output);
666  });
667  if (output_it != outputList.cend()) {
668  const simplecpp::Output &output = *output_it;
669  std::string file = Path::fromNativeSeparators(output.location.file());
672 
673  ErrorMessage::FileLocation loc1(file, output.location.line, output.location.col);
674 
675  ErrorMessage errmsg({std::move(loc1)},
676  "",
678  output.msg,
679  "syntaxError",
681  reportErr(errmsg);
682  return mExitCode;
683  }
684 
685  Preprocessor preprocessor(mSettings, *this);
686 
687  if (!preprocessor.loadFiles(tokens1, files))
688  return mExitCode;
689 
690  if (!mSettings.plistOutput.empty()) {
691  std::string filename2;
692  if (filename.find('/') != std::string::npos)
693  filename2 = filename.substr(filename.rfind('/') + 1);
694  else
695  filename2 = filename;
696  const std::size_t fileNameHash = std::hash<std::string> {}(filename);
697  filename2 = mSettings.plistOutput + filename2.substr(0, filename2.find('.')) + "_" + std::to_string(fileNameHash) + ".plist";
698  mPlistFile.open(filename2);
700  }
701 
702  std::string dumpProlog;
703  if (mSettings.dump || !mSettings.addons.empty()) {
704  dumpProlog += getDumpFileContentsRawTokens(files, tokens1);
705  }
706 
707  // Parse comments and then remove them
708  preprocessor.inlineSuppressions(tokens1, mSettings.supprs.nomsg);
709  if (mSettings.dump || !mSettings.addons.empty()) {
710  std::ostringstream oss;
711  mSettings.supprs.nomsg.dump(oss);
712  dumpProlog += oss.str();
713  }
714  tokens1.removeComments();
715  preprocessor.removeComments();
716 
717  if (!mSettings.buildDir.empty()) {
718  // Get toolinfo
719  std::ostringstream toolinfo;
720  toolinfo << CPPCHECK_VERSION_STRING;
721  toolinfo << (mSettings.severity.isEnabled(Severity::warning) ? 'w' : ' ');
722  toolinfo << (mSettings.severity.isEnabled(Severity::style) ? 's' : ' ');
723  toolinfo << (mSettings.severity.isEnabled(Severity::performance) ? 'p' : ' ');
724  toolinfo << (mSettings.severity.isEnabled(Severity::portability) ? 'p' : ' ');
725  toolinfo << (mSettings.severity.isEnabled(Severity::information) ? 'i' : ' ');
726  toolinfo << mSettings.userDefines;
727  mSettings.supprs.nomsg.dump(toolinfo);
728 
729  // Calculate hash so it can be compared with old hash / future hashes
730  const std::size_t hash = preprocessor.calculateHash(tokens1, toolinfo.str());
731  std::list<ErrorMessage> errors;
732  if (!mAnalyzerInformation.analyzeFile(mSettings.buildDir, filename, cfgname, hash, errors)) {
733  while (!errors.empty()) {
734  reportErr(errors.front());
735  errors.pop_front();
736  }
737  return mExitCode; // known results => no need to reanalyze file
738  }
739  }
740 
741  FilesDeleter filesDeleter;
742 
743  // write dump file xml prolog
744  std::ofstream fdump;
745  std::string dumpFile;
746  createDumpFile(mSettings, filename, fdump, dumpFile);
747  if (fdump.is_open()) {
748  fdump << dumpProlog;
749  if (!mSettings.dump)
750  filesDeleter.addFile(dumpFile);
751  }
752 
753  // Get directives
754  std::list<Directive> directives = preprocessor.createDirectives(tokens1);
755  preprocessor.simplifyPragmaAsm(&tokens1);
756 
757  preprocessor.setPlatformInfo(&tokens1);
758 
759  // Get configurations..
760  std::set<std::string> configurations;
762  Timer t("Preprocessor::getConfigs", mSettings.showtime, &s_timerResults);
763  configurations = preprocessor.getConfigs(tokens1);
764  } else {
765  configurations.insert(mSettings.userDefines);
766  }
767 
769  for (const std::string &config : configurations)
770  (void)preprocessor.getcode(tokens1, config, files, true);
771 
772  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  if (!mSettings.force && configurations.size() > mSettings.maxConfigs) {
793  tooManyConfigsError(Path::toNativeSeparators(filename),configurations.size());
794  } else {
795  mTooManyConfigs = true;
796  }
797  }
798 
799  std::set<unsigned long long> hashes;
800  int checkCount = 0;
801  bool hasValidConfig = false;
802  std::list<std::string> configurationError;
803  for (const std::string &currCfg : configurations) {
804  // bail out if terminated
805  if (Settings::terminated())
806  break;
807 
808  // Check only a few configurations (default 12), after that bail out, unless --force
809  // was used.
810  if (!mSettings.force && ++checkCount > mSettings.maxConfigs)
811  break;
812 
813  if (!mSettings.userDefines.empty()) {
815  const std::vector<std::string> v1(split(mSettings.userDefines, ";"));
816  for (const std::string &cfg: split(currCfg, ";")) {
817  if (std::find(v1.cbegin(), v1.cend(), cfg) == v1.cend()) {
818  mCurrentConfig += ";" + cfg;
819  }
820  }
821  } else {
822  mCurrentConfig = currCfg;
823  }
824 
826  Timer t("Preprocessor::getcode", mSettings.showtime, &s_timerResults);
827  std::string codeWithoutCfg = preprocessor.getcode(tokens1, mCurrentConfig, files, true);
828  t.stop();
829 
830  if (startsWith(codeWithoutCfg,"#file"))
831  codeWithoutCfg.insert(0U, "//");
832  std::string::size_type pos = 0;
833  while ((pos = codeWithoutCfg.find("\n#file",pos)) != std::string::npos)
834  codeWithoutCfg.insert(pos+1U, "//");
835  pos = 0;
836  while ((pos = codeWithoutCfg.find("\n#endfile",pos)) != std::string::npos)
837  codeWithoutCfg.insert(pos+1U, "//");
838  pos = 0;
839  while ((pos = codeWithoutCfg.find(Preprocessor::macroChar,pos)) != std::string::npos)
840  codeWithoutCfg[pos] = ' ';
841  reportOut(codeWithoutCfg);
842  continue;
843  }
844 
845  Tokenizer tokenizer(mSettings, *this);
847  tokenizer.setTimerResults(&s_timerResults);
848  tokenizer.setDirectives(directives); // TODO: how to avoid repeated copies?
849 
850  try {
851  // Create tokens, skip rest of iteration if failed
852  {
853  Timer timer("Tokenizer::createTokens", mSettings.showtime, &s_timerResults);
854  simplecpp::TokenList tokensP = preprocessor.preprocess(tokens1, mCurrentConfig, files, true);
855  tokenizer.list.createTokens(std::move(tokensP));
856  }
857  hasValidConfig = true;
858 
859  // locations macros
860  mLocationMacros.clear();
861  for (const Token* tok = tokenizer.tokens(); tok; tok = tok->next()) {
862  if (!tok->getMacroName().empty())
863  mLocationMacros[Location(files[tok->fileIndex()], tok->linenr())].emplace(tok->getMacroName());
864  }
865 
866  // If only errors are printed, print filename after the check
867  if (!mSettings.quiet && (!mCurrentConfig.empty() || checkCount > 1)) {
868  std::string fixedpath = Path::simplifyPath(filename);
869  fixedpath = Path::toNativeSeparators(std::move(fixedpath));
870  mErrorLogger.reportOut("Checking " + fixedpath + ": " + mCurrentConfig + "...", Color::FgGreen);
871  }
872 
873  if (!tokenizer.tokens())
874  continue;
875 
876  // skip rest of iteration if just checking configuration
878  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  if (!tokenizer.simplifyTokens1(mCurrentConfig))
887  continue;
888 
889  // dump xml if --dump
890  if ((mSettings.dump || !mSettings.addons.empty()) && fdump.is_open()) {
891  fdump << "<dump cfg=\"" << ErrorLogger::toxml(mCurrentConfig) << "\">" << std::endl;
892  fdump << " <standards>" << std::endl;
893  fdump << " <c version=\"" << mSettings.standards.getC() << "\"/>" << std::endl;
894  fdump << " <cpp version=\"" << mSettings.standards.getCPP() << "\"/>" << std::endl;
895  fdump << " </standards>" << std::endl;
896  preprocessor.dump(fdump);
897  tokenizer.dump(fdump);
898  fdump << "</dump>" << std::endl;
899  }
900 
901  // Need to call this even if the hash will skip this configuration
903 
904  // Skip if we already met the same simplified token list
905  if (mSettings.force || mSettings.maxConfigs > 1) {
906  const std::size_t hash = tokenizer.list.calculateHash();
907  if (hashes.find(hash) != hashes.end()) {
910  continue;
911  }
912  hashes.insert(hash);
913  }
914 
915  // Check normal tokens
916  checkNormalTokens(tokenizer);
917  } catch (const simplecpp::Output &o) {
918  // #error etc during preprocessing
919  configurationError.push_back((mCurrentConfig.empty() ? "\'\'" : mCurrentConfig) + " : [" + o.location.file() + ':' + std::to_string(o.location.line) + "] " + o.msg);
920  --checkCount; // don't count invalid configurations
921 
922  if (!hasValidConfig && currCfg == *configurations.rbegin()) {
923  // If there is no valid configuration then report error..
924  std::string file = Path::fromNativeSeparators(o.location.file());
927 
928  ErrorMessage::FileLocation loc1(file, o.location.line, o.location.col);
929 
930  ErrorMessage errmsg({std::move(loc1)},
931  filename,
933  o.msg,
934  "preprocessorErrorDirective",
936  reportErr(errmsg);
937  }
938  continue;
939 
940  } catch (const TerminateException &) {
941  // Analysis is terminated
942  return mExitCode;
943 
944  } catch (const InternalError &e) {
945  ErrorMessage errmsg = ErrorMessage::fromInternalError(e, &tokenizer.list, filename);
946  reportErr(errmsg);
947  }
948  }
949 
950  if (!hasValidConfig && configurations.size() > 1 && mSettings.severity.isEnabled(Severity::information)) {
951  std::string msg;
952  msg = "This file is not analyzed. Cppcheck failed to extract a valid configuration. Use -v for more details.";
953  msg += "\nThis file is not analyzed. Cppcheck failed to extract a valid configuration. The tested configurations have these preprocessor errors:";
954  for (const std::string &s : configurationError)
955  msg += '\n' + s;
956 
957  const std::string locFile = Path::toNativeSeparators(filename);
958  ErrorMessage::FileLocation loc(locFile, 0, 0);
959  ErrorMessage errmsg({std::move(loc)},
960  locFile,
962  msg,
963  "noValidConfiguration",
965  reportErr(errmsg);
966  }
967 
968  // dumped all configs, close root </dumps> element now
969  if (fdump.is_open()) {
970  fdump << "</dumps>" << std::endl;
971  fdump.close();
972  }
973 
974  executeAddons(dumpFile, Path::simplifyPath(filename));
975 
976  } catch (const TerminateException &) {
977  // Analysis is terminated
978  return mExitCode;
979  } catch (const std::runtime_error &e) {
980  internalError(filename, std::string("Checking file failed: ") + e.what());
981  } catch (const std::bad_alloc &) {
982  internalError(filename, "Checking file failed: out of memory");
983  } catch (const InternalError &e) {
984  const ErrorMessage errmsg = ErrorMessage::fromInternalError(e, nullptr, filename, "Bailing out from analysis: Checking file failed");
985  reportErr(errmsg);
986  }
987 
988  if (!mSettings.buildDir.empty()) {
990  }
991 
992  // In jointSuppressionReport mode, unmatched suppressions are
993  // collected after all files are processed
996  }
997 
998  mErrorList.clear();
999 
1002 
1003  return mExitCode;
1004 }
1005 
1006 // TODO: replace with ErrorMessage::fromInternalError()
1007 void CppCheck::internalError(const std::string &filename, const std::string &msg)
1008 {
1009  const std::string fullmsg("Bailing out from analysis: " + msg);
1010 
1011  ErrorMessage::FileLocation loc1(filename, 0, 0);
1012 
1013  ErrorMessage errmsg({std::move(loc1)},
1014  emptyString,
1016  fullmsg,
1017  "internalError",
1019 
1020  mErrorLogger.reportErr(errmsg);
1021 }
1022 
1023 //---------------------------------------------------------------------------
1024 // CppCheck - A function that checks a normal token list
1025 //---------------------------------------------------------------------------
1026 
1028 {
1029  CheckUnusedFunctions unusedFunctionsChecker;
1030 
1031  // TODO: this should actually be the behavior if only "--enable=unusedFunction" is specified - see #10648
1032  const char* unusedFunctionOnly = std::getenv("UNUSEDFUNCTION_ONLY");
1033  const bool doUnusedFunctionOnly = unusedFunctionOnly && (std::strcmp(unusedFunctionOnly, "1") == 0);
1034 
1035  if (!doUnusedFunctionOnly) {
1036  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  for (Check *check : Check::instances()) {
1041  if (Settings::terminated())
1042  return;
1043 
1044  if (maxTime > 0 && std::time(nullptr) > maxTime) {
1045  if (mSettings.debugwarnings) {
1046  ErrorMessage::FileLocation loc(tokenizer.list.getFiles()[0], 0, 0);
1047  ErrorMessage errmsg({std::move(loc)},
1048  emptyString,
1050  "Checks maximum time exceeded",
1051  "checksMaxTime",
1053  reportErr(errmsg);
1054  }
1055  return;
1056  }
1057 
1058  Timer timerRunChecks(check->name() + "::runChecks", mSettings.showtime, &s_timerResults);
1059  check->runChecks(tokenizer, this);
1060  }
1061  }
1062 
1064  unusedFunctionsChecker.parseTokens(tokenizer, mSettings);
1065  }
1067  mUnusedFunctionsCheck->parseTokens(tokenizer, mSettings);
1068  }
1069 
1070  if (mSettings.clang) {
1071  // TODO: Use CTU for Clang analysis
1072  return;
1073  }
1074 
1075  if (mSettings.useSingleJob() || !mSettings.buildDir.empty()) {
1076  // Analyse the tokens..
1077 
1078  if (CTU::FileInfo * const fi1 = CTU::getFileInfo(tokenizer)) {
1079  if (!mSettings.buildDir.empty())
1080  mAnalyzerInformation.setFileInfo("ctu", fi1->toString());
1081  if (mSettings.useSingleJob())
1082  mFileInfo.push_back(fi1);
1083  else
1084  delete fi1;
1085  }
1086 
1087  if (!doUnusedFunctionOnly) {
1088  // cppcheck-suppress shadowFunction - TODO: fix this
1089  for (const Check *check : Check::instances()) {
1090  if (Check::FileInfo * const fi = check->getFileInfo(tokenizer, mSettings)) {
1091  if (!mSettings.buildDir.empty())
1092  mAnalyzerInformation.setFileInfo(check->name(), fi->toString());
1093  if (mSettings.useSingleJob())
1094  mFileInfo.push_back(fi);
1095  else
1096  delete fi;
1097  }
1098  }
1099  }
1100  }
1101 
1103  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,
1280  msg,
1281  "pcre_compile",
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,
1301  msg,
1302  "pcre_study",
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,
1324  std::string("pcre_exec failed: ") + errorMessage,
1325  "pcre_exec",
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,
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 void CppCheck::executeAddons(const std::string& dumpFile, const std::string& file0)
1380 {
1381  if (!dumpFile.empty()) {
1382  std::vector<std::string> f{dumpFile};
1383  executeAddons(f, file0);
1384  }
1385 }
1386 
1387 void CppCheck::executeAddons(const std::vector<std::string>& files, const std::string& file0)
1388 {
1389  if (mSettings.addons.empty() || files.empty())
1390  return;
1391 
1392  FilesDeleter filesDeleter;
1393 
1394  std::string fileList;
1395 
1396  if (files.size() >= 2 || endsWith(files[0], ".ctu-info")) {
1397  fileList = Path::getPathFromFilename(files[0]) + FILELIST + std::to_string(mSettings.pid);
1398  filesDeleter.addFile(fileList);
1399  std::ofstream fout(fileList);
1400  for (const std::string& f: files)
1401  fout << f << std::endl;
1402  }
1403 
1404  // ensure all addons have already been resolved - TODO: remove when settings are const after creation
1405  assert(mSettings.addonInfos.size() == mSettings.addons.size());
1406 
1407  for (const AddonInfo &addonInfo : mSettings.addonInfos) {
1408  if (addonInfo.name != "misra" && !addonInfo.ctu && endsWith(files.back(), ".ctu-info"))
1409  continue;
1410 
1411  const std::vector<picojson::value> results =
1412  executeAddon(addonInfo, mSettings.addonPython, fileList.empty() ? files[0] : fileList, mSettings.premiumArgs, mExecuteCommand);
1413 
1414  const bool misraC2023 = mSettings.premiumArgs.find("--misra-c-2023") != std::string::npos;
1415 
1416  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  picojson::object obj = res.get<picojson::object>();
1420 
1421  ErrorMessage errmsg;
1422 
1423  if (obj.count("file") > 0) {
1424  std::string fileName = obj["file"].get<std::string>();
1425  const int64_t lineNumber = obj["linenr"].get<int64_t>();
1426  const int64_t column = obj["column"].get<int64_t>();
1427  errmsg.callStack.emplace_back(std::move(fileName), lineNumber, column);
1428  } else if (obj.count("loc") > 0) {
1429  for (const picojson::value &locvalue: obj["loc"].get<picojson::array>()) {
1430  picojson::object loc = locvalue.get<picojson::object>();
1431  std::string fileName = loc["file"].get<std::string>();
1432  const int64_t lineNumber = loc["linenr"].get<int64_t>();
1433  const int64_t column = loc["column"].get<int64_t>();
1434  std::string info = loc["info"].get<std::string>();
1435  errmsg.callStack.emplace_back(std::move(fileName), std::move(info), lineNumber, column);
1436  }
1437  }
1438 
1439  errmsg.id = obj["addon"].get<std::string>() + "-" + obj["errorId"].get<std::string>();
1440  if (misraC2023 && startsWith(errmsg.id, "misra-c2012-"))
1441  errmsg.id = "misra-c2023-" + errmsg.id.substr(12);
1442  errmsg.setmsg(mSettings.getMisraRuleText(errmsg.id, obj["message"].get<std::string>()));
1443  const std::string severity = obj["severity"].get<std::string>();
1444  errmsg.severity = severityFromString(severity);
1445  if (errmsg.severity == Severity::none || errmsg.severity == Severity::internal) {
1446  if (!endsWith(errmsg.id, "-logChecker"))
1447  continue;
1448  errmsg.severity = Severity::internal;
1449  }
1450  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  if (!isPremiumCodingStandardId(errmsg.id))
1454  continue;
1455  }
1456  errmsg.file0 = file0;
1457 
1458  reportErr(errmsg);
1459  }
1460  }
1461 }
1462 
1463 void CppCheck::executeAddonsWholeProgram(const std::list<FileWithDetails> &files)
1464 {
1465  if (mSettings.addons.empty())
1466  return;
1467 
1468  std::vector<std::string> ctuInfoFiles;
1469  for (const auto &f: files) {
1470  const std::string &dumpFileName = getDumpFileName(mSettings, f.path());
1471  ctuInfoFiles.push_back(getCtuInfoFileName(dumpFileName));
1472  }
1473 
1474  try {
1475  executeAddons(ctuInfoFiles, "");
1476  } catch (const InternalError& e) {
1477  const ErrorMessage errmsg = ErrorMessage::fromInternalError(e, nullptr, "", "Bailing out from analysis: Whole program analysis failed");
1478  reportErr(errmsg);
1479  }
1480 
1481  if (mSettings.buildDir.empty()) {
1482  for (const std::string &f: ctuInfoFiles)
1483  std::remove(f.c_str());
1484  }
1485 }
1486 
1488 {
1489  return mSettings;
1490 }
1491 
1492 void CppCheck::tooManyConfigsError(const std::string &file, const int numberOfConfigurations)
1493 {
1495  return;
1496 
1497  mTooManyConfigs = false;
1498 
1499  if (mSettings.severity.isEnabled(Severity::information) && file.empty())
1500  return;
1501 
1502  std::list<ErrorMessage::FileLocation> loclist;
1503  if (!file.empty()) {
1504  loclist.emplace_back(file, 0, 0);
1505  }
1506 
1507  std::ostringstream msg;
1508  msg << "Too many #ifdef configurations - cppcheck only checks " << mSettings.maxConfigs;
1509  if (numberOfConfigurations > mSettings.maxConfigs)
1510  msg << " of " << numberOfConfigurations << " configurations. Use --force to check all configurations.\n";
1511  if (file.empty())
1512  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  "increase the checking time.";
1517  if (file.empty())
1518  msg << " For more details, use --enable=information.";
1519 
1520 
1521  ErrorMessage errmsg(std::move(loclist),
1522  emptyString,
1524  msg.str(),
1525  "toomanyconfigs", CWE398,
1527 
1528  reportErr(errmsg);
1529 }
1530 
1531 void CppCheck::purgedConfigurationMessage(const std::string &file, const std::string& configuration)
1532 {
1533  mTooManyConfigs = false;
1534 
1535  if (mSettings.severity.isEnabled(Severity::information) && file.empty())
1536  return;
1537 
1538  std::list<ErrorMessage::FileLocation> loclist;
1539  if (!file.empty()) {
1540  loclist.emplace_back(file, 0, 0);
1541  }
1542 
1543  ErrorMessage errmsg(std::move(loclist),
1544  emptyString,
1546  "The configuration '" + configuration + "' was not checked because its code equals another one.",
1547  "purgedConfiguration",
1549 
1550  reportErr(errmsg);
1551 }
1552 
1553 //---------------------------------------------------------------------------
1554 
1555 // TODO: part of this logic is duplicated in Executor::hasToLog()
1557 {
1558  if (msg.severity == Severity::internal) {
1559  mErrorLogger.reportErr(msg);
1560  return;
1561  }
1562 
1563  if (!mSettings.library.reportErrors(msg.file0))
1564  return;
1565 
1566  std::set<std::string> macroNames;
1567  if (!msg.callStack.empty()) {
1568  const std::string &file = msg.callStack.back().getfile(false);
1569  int lineNumber = msg.callStack.back().line;
1570  const auto it = mLocationMacros.find(Location(file, lineNumber));
1571  if (it != mLocationMacros.cend())
1572  macroNames = it->second;
1573  }
1574 
1575  // TODO: only convert if necessary
1576  const auto errorMessage = SuppressionList::ErrorMessage::fromErrorMessage(msg, macroNames);
1577 
1579  // Safety: Report critical errors to ErrorLogger
1581  mExitCode = 1;
1582 
1584  // Report with internal severity to signal that there is this critical error but
1585  // it is suppressed
1586  ErrorMessage temp(msg);
1588  mErrorLogger.reportErr(temp);
1589  } else {
1590  // Report critical error that is not explicitly suppressed
1591  mErrorLogger.reportErr(msg);
1592  }
1593  }
1594  return;
1595  }
1596 
1597  // TODO: there should be no need for the verbose and default messages here
1598  std::string errmsg = msg.toString(mSettings.verbose);
1599  if (errmsg.empty())
1600  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  if (!mErrorList.emplace(std::move(errmsg)).second)
1606  return;
1607 
1608  if (!mSettings.buildDir.empty())
1610 
1611  if (!mSettings.supprs.nofail.isSuppressed(errorMessage) && !mSettings.supprs.nomsg.isSuppressed(errorMessage)) {
1612  mExitCode = 1;
1613  }
1614 
1615  mErrorLogger.reportErr(msg);
1616  // check if plistOutput should be populated and the current output file is open and the error is not suppressed
1617  if (!mSettings.plistOutput.empty() && mPlistFile.is_open() && !mSettings.supprs.nomsg.isSuppressed(errorMessage)) {
1618  // add error to plist output file
1620  }
1621 }
1622 
1623 void CppCheck::reportOut(const std::string &outmsg, Color c)
1624 {
1625  mErrorLogger.reportOut(outmsg, c);
1626 }
1627 
1628 void CppCheck::reportProgress(const std::string &filename, const char stage[], const std::size_t value)
1629 {
1630  mErrorLogger.reportProgress(filename, stage, value);
1631 }
1632 
1634 {
1635  Settings s;
1641 
1642  CppCheck cppcheck(errorlogger, true, nullptr);
1643  cppcheck.purgedConfigurationMessage(emptyString,emptyString);
1644  cppcheck.mTooManyConfigs = true;
1645  cppcheck.tooManyConfigsError(emptyString,0U);
1646  // TODO: add functions to get remaining error messages
1647 
1648  // call all "getErrorMessages" in all registered Check classes
1649  for (std::list<Check *>::const_iterator it = Check::instances().cbegin(); it != Check::instances().cend(); ++it)
1650  (*it)->getErrorMessages(&errorlogger, &s);
1651 
1653  Preprocessor::getErrorMessages(errorlogger, s);
1654 }
1655 
1656 void CppCheck::analyseClangTidy(const FileSettings &fileSettings)
1657 {
1658  std::string allIncludes;
1659  for (const std::string &inc : fileSettings.includePaths) {
1660  allIncludes = allIncludes + "-I\"" + inc + "\" ";
1661  }
1662 
1663  const std::string allDefines = getDefinesFlags(fileSettings.defines);
1664 
1665 #ifdef _WIN32
1666  constexpr char exe[] = "clang-tidy.exe";
1667 #else
1668  constexpr char exe[] = "clang-tidy";
1669 #endif
1670 
1671  const std::string args = "-quiet -checks=*,-clang-analyzer-*,-llvm* \"" + fileSettings.filename() + "\" -- " + allIncludes + allDefines;
1672  std::string output;
1673  if (const int exitcode = mExecuteCommand(exe, split(args), emptyString, output)) {
1674  std::cerr << "Failed to execute '" << exe << "' (exitcode: " << std::to_string(exitcode) << ")" << std::endl;
1675  return;
1676  }
1677 
1678  // parse output and create error messages
1679  std::istringstream istr(output);
1680  std::string line;
1681 
1682  if (!mSettings.buildDir.empty()) {
1683  const std::string analyzerInfoFile = AnalyzerInformation::getAnalyzerInfoFile(mSettings.buildDir, fileSettings.filename(), emptyString);
1684  std::ofstream fcmd(analyzerInfoFile + ".clang-tidy-cmd");
1685  fcmd << istr.str();
1686  }
1687 
1688  while (std::getline(istr, line)) {
1689  if (line.find("error") == std::string::npos && line.find("warning") == std::string::npos)
1690  continue;
1691 
1692  std::size_t endColumnPos = line.find(": error:");
1693  if (endColumnPos == std::string::npos) {
1694  endColumnPos = line.find(": warning:");
1695  }
1696 
1697  const std::size_t endLinePos = line.rfind(':', endColumnPos-1);
1698  const std::size_t endNamePos = line.rfind(':', endLinePos - 1);
1699  const std::size_t endMsgTypePos = line.find(':', endColumnPos + 2);
1700  const std::size_t endErrorPos = line.rfind('[', std::string::npos);
1701  if (endLinePos==std::string::npos || endNamePos==std::string::npos || endMsgTypePos==std::string::npos || endErrorPos==std::string::npos)
1702  continue;
1703 
1704  const std::string lineNumString = line.substr(endNamePos + 1, endLinePos - endNamePos - 1);
1705  const std::string columnNumString = line.substr(endLinePos + 1, endColumnPos - endLinePos - 1);
1706  const std::string messageString = line.substr(endMsgTypePos + 1, endErrorPos - endMsgTypePos - 1);
1707  const std::string errorString = line.substr(endErrorPos, line.length());
1708 
1709  std::string fixedpath = Path::simplifyPath(line.substr(0, endNamePos));
1710  const int64_t lineNumber = strToInt<int64_t>(lineNumString);
1711  const int64_t column = strToInt<int64_t>(columnNumString);
1712  fixedpath = Path::toNativeSeparators(std::move(fixedpath));
1713 
1714  ErrorMessage errmsg;
1715  errmsg.callStack.emplace_back(fixedpath, lineNumber, column);
1716 
1717  errmsg.id = "clang-tidy-" + errorString.substr(1, errorString.length() - 2);
1718  if (errmsg.id.find("performance") != std::string::npos)
1720  else if (errmsg.id.find("portability") != std::string::npos)
1722  else if (errmsg.id.find("cert") != std::string::npos || errmsg.id.find("misc") != std::string::npos || errmsg.id.find("unused") != std::string::npos)
1723  errmsg.severity = Severity::warning;
1724  else
1725  errmsg.severity = Severity::style;
1726 
1727  errmsg.file0 = std::move(fixedpath);
1728  errmsg.setmsg(messageString);
1729  reportErr(errmsg);
1730  }
1731 }
1732 
1734 {
1735  bool errors = false;
1736  // Init CTU
1738  // Analyse the tokens
1739  CTU::FileInfo ctu;
1740  for (const Check::FileInfo *fi : mFileInfo) {
1741  const auto *fi2 = dynamic_cast<const CTU::FileInfo *>(fi);
1742  if (fi2) {
1743  ctu.functionCalls.insert(ctu.functionCalls.end(), fi2->functionCalls.cbegin(), fi2->functionCalls.cend());
1744  ctu.nestedCalls.insert(ctu.nestedCalls.end(), fi2->nestedCalls.cbegin(), fi2->nestedCalls.cend());
1745  }
1746  }
1747 
1748  // cppcheck-suppress shadowFunction - TODO: fix this
1749  for (Check *check : Check::instances())
1750  errors |= check->analyseWholeProgram(&ctu, mFileInfo, mSettings, *this); // TODO: ctu
1751 
1753  errors |= mUnusedFunctionsCheck->check(mSettings, *this);
1754 
1755  return errors && (mExitCode > 0);
1756 }
1757 
1758 unsigned int CppCheck::analyseWholeProgram(const std::string &buildDir, const std::list<FileWithDetails> &files, const std::list<FileSettings>& fileSettings)
1759 {
1760  executeAddonsWholeProgram(files); // TODO: pass FileSettings
1761  if (buildDir.empty()) {
1762  removeCtuInfoFiles(files, fileSettings);
1763  return mExitCode;
1764  }
1767  std::list<Check::FileInfo*> fileInfoList;
1768  CTU::FileInfo ctuFileInfo;
1769 
1770  // Load all analyzer info data..
1771  const std::string filesTxt(buildDir + "/files.txt");
1772  std::ifstream fin(filesTxt);
1773  std::string filesTxtLine;
1774  while (std::getline(fin, filesTxtLine)) {
1775  const std::string::size_type firstColon = filesTxtLine.find(':');
1776  if (firstColon == std::string::npos)
1777  continue;
1778  const std::string::size_type lastColon = filesTxtLine.rfind(':');
1779  if (firstColon == lastColon)
1780  continue;
1781  const std::string xmlfile = buildDir + '/' + filesTxtLine.substr(0,firstColon);
1782  //const std::string sourcefile = filesTxtLine.substr(lastColon+1);
1783 
1784  tinyxml2::XMLDocument doc;
1785  const tinyxml2::XMLError error = doc.LoadFile(xmlfile.c_str());
1786  if (error != tinyxml2::XML_SUCCESS)
1787  continue;
1788 
1789  const tinyxml2::XMLElement * const rootNode = doc.FirstChildElement();
1790  if (rootNode == nullptr)
1791  continue;
1792 
1793  for (const tinyxml2::XMLElement *e = rootNode->FirstChildElement(); e; e = e->NextSiblingElement()) {
1794  if (std::strcmp(e->Name(), "FileInfo") != 0)
1795  continue;
1796  const char *checkClassAttr = e->Attribute("check");
1797  if (!checkClassAttr)
1798  continue;
1799  if (std::strcmp(checkClassAttr, "ctu") == 0) {
1800  ctuFileInfo.loadFromXml(e);
1801  continue;
1802  }
1803  // cppcheck-suppress shadowFunction - TODO: fix this
1804  for (const Check *check : Check::instances()) {
1805  if (checkClassAttr == check->name())
1806  fileInfoList.push_back(check->loadFileInfoFromXml(e));
1807  }
1808  }
1809  }
1810 
1811  // Set CTU max depth
1813 
1814  // Analyse the tokens
1815  // cppcheck-suppress shadowFunction - TODO: fix this
1816  for (Check *check : Check::instances())
1817  check->analyseWholeProgram(&ctuFileInfo, fileInfoList, mSettings, *this);
1818 
1820  mUnusedFunctionsCheck->check(mSettings, *this);
1821 
1822  for (Check::FileInfo *fi : fileInfoList)
1823  delete fi;
1824 
1825  return mExitCode;
1826 }
1827 
1828 void CppCheck::removeCtuInfoFiles(const std::list<FileWithDetails> &files, const std::list<FileSettings>& fileSettings)
1829 {
1830  if (mSettings.buildDir.empty()) {
1831  for (const auto& f: files) {
1832  const std::string &dumpFileName = getDumpFileName(mSettings, f.path());
1833  const std::string &ctuInfoFileName = getCtuInfoFileName(dumpFileName);
1834  std::remove(ctuInfoFileName.c_str());
1835  }
1836  for (const auto& fs: fileSettings) {
1837  const std::string &dumpFileName = getDumpFileName(mSettings, fs.filename());
1838  const std::string &ctuInfoFileName = getCtuInfoFileName(dumpFileName);
1839  std::remove(ctuInfoFileName.c_str());
1840  }
1841  }
1842 }
1843 
1844 // cppcheck-suppress unusedFunction - only used in tests
1846 {
1848 }
1849 
1851 {
1853 }
1854 
1855 bool CppCheck::isPremiumCodingStandardId(const std::string& id) const {
1856  if (mSettings.premiumArgs.find("--misra") != std::string::npos) {
1857  if (startsWith(id, "misra-") || startsWith(id, "premium-misra-"))
1858  return true;
1859  }
1860  if (mSettings.premiumArgs.find("--cert") != std::string::npos && startsWith(id, "premium-cert-"))
1861  return true;
1862  if (mSettings.premiumArgs.find("--autosar") != std::string::npos && startsWith(id, "premium-autosar-"))
1863  return true;
1864  return false;
1865 }
1866 
1867 std::string CppCheck::getDumpFileContentsRawTokens(const std::vector<std::string>& files, const simplecpp::TokenList& tokens1) const {
1868  std::string dumpProlog;
1869  dumpProlog += " <rawtokens>\n";
1870  for (unsigned int i = 0; i < files.size(); ++i) {
1871  dumpProlog += " <file index=\"";
1872  dumpProlog += std::to_string(i);
1873  dumpProlog += "\" name=\"";
1874  dumpProlog += ErrorLogger::toxml(Path::getRelativePath(files[i], mSettings.basePaths));
1875  dumpProlog += "\"/>\n";
1876  }
1877  for (const simplecpp::Token *tok = tokens1.cfront(); tok; tok = tok->next) {
1878  dumpProlog += " <tok ";
1879 
1880  dumpProlog += "fileIndex=\"";
1881  dumpProlog += std::to_string(tok->location.fileIndex);
1882  dumpProlog += "\" ";
1883 
1884  dumpProlog += "linenr=\"";
1885  dumpProlog += std::to_string(tok->location.line);
1886  dumpProlog += "\" ";
1887 
1888  dumpProlog +="column=\"";
1889  dumpProlog += std::to_string(tok->location.col);
1890  dumpProlog += "\" ";
1891 
1892  dumpProlog += "str=\"";
1893  dumpProlog += ErrorLogger::toxml(tok->str());
1894  dumpProlog += "\"";
1895 
1896  dumpProlog += "/>\n";
1897  }
1898  dumpProlog += " </rawtokens>\n";
1899  return dumpProlog;
1900 }
static std::string getAnalyzerInfoFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg)
void reportErr(const ErrorMessage &msg)
void setFileInfo(const std::string &check, const std::string &fileInfo)
void close()
Close current TU.analyzerinfo file.
bool analyzeFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg, std::size_t hash, std::list< ErrorMessage > &errors)
std::list< NestedCall > nestedCalls
Definition: ctu.h:127
void loadFromXml(const tinyxml2::XMLElement *xmlElement)
Definition: ctu.cpp:232
std::list< FunctionCall > functionCalls
Definition: ctu.h:126
Check for functions never called.
void parseTokens(const Tokenizer &tokenizer, const Settings &settings)
static void getErrorMessages(ErrorLogger &errorLogger)
static void analyseWholeProgram(const Settings &settings, ErrorLogger &errorLogger, const std::string &buildDir)
std::string analyzerInfo() const
Base class used for whole-program analysis.
Definition: check.h:103
Interface class that cppcheck uses to communicate with the checks.
Definition: check.h:59
static std::list< Check * > & instances()
List of registered check classes.
Definition: check.cpp:89
This is the base class which will use other classes to do static code analysis for C and C++ code to ...
Definition: cppcheck.h:60
bool analyseWholeProgram()
Analyse whole program, run this after all TUs has been scanned.
Definition: cppcheck.cpp:1733
ErrorLogger & mErrorLogger
Definition: cppcheck.h:231
std::unordered_set< std::string > mErrorList
Definition: cppcheck.h:226
static void resetTimerResults()
Definition: cppcheck.cpp:1845
unsigned int checkClang(const std::string &path)
Definition: cppcheck.cpp:414
std::unique_ptr< CheckUnusedFunctions > mUnusedFunctionsCheck
Definition: cppcheck.h:256
static void printTimerResults(SHOWTIME_MODES mode)
Definition: cppcheck.cpp:1850
void checkNormalTokens(const Tokenizer &tokenizer)
Check normal tokens.
Definition: cppcheck.cpp:1027
Settings & settings()
Get reference to current settings.
Definition: cppcheck.cpp:1487
void removeCtuInfoFiles(const std::list< FileWithDetails > &files, const std::list< FileSettings > &fileSettings)
Remove *.ctu-info files.
Definition: cppcheck.cpp:1828
AnalyzerInformation mAnalyzerInformation
Definition: cppcheck.h:249
Settings mSettings
Definition: cppcheck.h:227
unsigned int checkFile(const std::string &filename, const std::string &cfgname, std::istream *fileStream=nullptr)
Check a file using stream.
Definition: cppcheck.cpp:601
std::list< Check::FileInfo * > mFileInfo
File info used for whole program analysis.
Definition: cppcheck.h:247
std::function< int(std::string, std::vector< std::string >, std::string, std::string &)> ExecuteCmdFn
Definition: cppcheck.h:62
static void getErrorMessages(ErrorLogger &errorlogger)
Call all "getErrorMessages" in all registered Check classes.
Definition: cppcheck.cpp:1633
void executeAddonsWholeProgram(const std::list< FileWithDetails > &files)
Execute addons.
Definition: cppcheck.cpp:1463
std::pair< std::string, int > Location
Definition: cppcheck.h:236
bool mUseGlobalSuppressions
Definition: cppcheck.h:241
std::string mCurrentConfig
Current preprocessor configuration.
Definition: cppcheck.h:234
void internalError(const std::string &filename, const std::string &msg)
There has been an internal error => Report information message.
Definition: cppcheck.cpp:1007
static const char * version()
Returns current version number as a string.
Definition: cppcheck.cpp:354
unsigned int mExitCode
Definition: cppcheck.h:239
void reportOut(const std::string &outmsg, Color c=Color::Reset) override
Information about progress is directed here.
Definition: cppcheck.cpp:1623
std::string getDumpFileContentsRawTokens(const std::vector< std::string > &files, const simplecpp::TokenList &tokens1) const
Get dumpfile <rawtokens> contents, this is only public for testing purposes.
Definition: cppcheck.cpp:1867
unsigned int check(const std::string &path)
This starts the actual checking.
Definition: cppcheck.cpp:542
std::ofstream mPlistFile
Definition: cppcheck.h:254
CppCheck(ErrorLogger &errorLogger, bool useGlobalSuppressions, ExecuteCmdFn executeCommand)
Constructor.
Definition: cppcheck.cpp:333
void reportProgress(const std::string &filename, const char stage[], const std::size_t value) override
Report progress to client.
Definition: cppcheck.cpp:1628
~CppCheck() override
Destructor.
Definition: cppcheck.cpp:341
std::map< Location, std::set< std::string > > mLocationMacros
Definition: cppcheck.h:237
void analyseClangTidy(const FileSettings &fileSettings)
Analyze all files using clang-tidy.
Definition: cppcheck.cpp:1656
bool isPremiumCodingStandardId(const std::string &id) const
Definition: cppcheck.cpp:1855
void executeAddons(const std::vector< std::string > &files, const std::string &file0)
Execute addons.
Definition: cppcheck.cpp:1387
bool mTooManyConfigs
Are there too many configs?
Definition: cppcheck.h:244
ExecuteCmdFn mExecuteCommand
Callback for executing a shell command (exe, args, output)
Definition: cppcheck.h:252
void purgedConfigurationMessage(const std::string &file, const std::string &configuration)
Definition: cppcheck.cpp:1531
void tooManyConfigsError(const std::string &file, const int numberOfConfigurations)
Definition: cppcheck.cpp:1492
static const char * extraVersion()
Returns extra version info as a string.
Definition: cppcheck.cpp:359
void reportErr(const ErrorMessage &msg) override
Errors and warnings are directed here.
Definition: cppcheck.cpp:1556
This is an interface, which the class responsible of error logging should implement.
Definition: errorlogger.h:214
static bool isCriticalErrorId(const std::string &id)
Definition: errorlogger.h:264
static std::string plistData(const ErrorMessage &msg)
static const char * plistFooter()
Definition: errorlogger.h:258
virtual void reportErr(const ErrorMessage &msg)=0
Information about found errors and warnings is directed here.
static std::string toxml(const std::string &str)
Convert XML-sensitive characters into XML entities.
virtual void reportOut(const std::string &outmsg, Color c=Color::Reset)=0
Information about progress is directed here.
static std::string plistHeader(const std::string &version, const std::vector< std::string > &files)
virtual void reportProgress(const std::string &filename, const char stage[], const std::size_t value)
Report progress to client.
Definition: errorlogger.h:241
File name and line number.
Definition: errorlogger.h:55
Wrapper for error messages, provided by reportErr()
Definition: errorlogger.h:48
Severity severity
Definition: errorlogger.h:170
std::string toString(bool verbose, const std::string &templateFormat=emptyString, const std::string &templateLocation=emptyString) const
Format the error message into a string.
std::string file0
For GUI rechecking; source file (not header)
Definition: errorlogger.h:168
std::list< FileLocation > callStack
Definition: errorlogger.h:164
std::string id
Definition: errorlogger.h:165
static ErrorMessage fromInternalError(const InternalError &internalError, const TokenList *tokenList, const std::string &filename, const std::string &msg=emptyString)
void setmsg(const std::string &msg)
set short and verbose messages
bool reportErrors(const std::string &path) const
Definition: library.cpp:1588
bool markupFile(const std::string &path) const
Definition: library.cpp:1577
static std::string simplifyPath(std::string originalPath)
Simplify path "foo/bar/.." => "foo".
Definition: path.cpp:83
static std::string fromNativeSeparators(std::string path)
Convert path to use internal path separators.
Definition: path.cpp:75
static std::string toNativeSeparators(std::string path)
Convert path to use native separators.
Definition: path.cpp:62
static Standards::Language identify(const std::string &path, bool *header=nullptr)
Identify the language based on the file extension.
Definition: path.cpp:248
static std::string getPathFromFilename(const std::string &filename)
Lookup the path part from a filename (e.g., '/tmp/a.h' -> '/tmp/', 'a.h' -> '')
Definition: path.cpp:88
static std::string getRelativePath(const std::string &absolutePath, const std::vector< std::string > &basePaths)
Create a relative path from an absolute one, if absolute path is inside the basePaths.
Definition: path.cpp:181
nonneg int short_bit
bits in char
Definition: platform.h:86
nonneg int int_bit
bits in short
Definition: platform.h:87
nonneg int long_bit
bits in int
Definition: platform.h:88
std::size_t sizeof_pointer
Definition: platform.h:102
nonneg int char_bit
Definition: platform.h:85
const char * toString() const
Definition: platform.h:148
nonneg int long_long_bit
bits in long
Definition: platform.h:89
bool set(Type t)
set the platform type for predefined platforms - deprecated use set(const std::string&,...
Definition: platform.cpp:36
The cppcheck preprocessor.
Definition: preprocessor.h:71
void setPlatformInfo(simplecpp::TokenList *tokens) const
void simplifyPragmaAsm(simplecpp::TokenList *tokenList) const
bool loadFiles(const simplecpp::TokenList &rawtokens, std::vector< std::string > &files)
std::set< std::string > getConfigs(const simplecpp::TokenList &tokens) const
void removeComments()
static void getErrorMessages(ErrorLogger &errorLogger, const Settings &settings)
std::size_t calculateHash(const simplecpp::TokenList &tokens1, const std::string &toolinfo) const
Calculate HASH.
std::string getcode(const simplecpp::TokenList &tokens1, const std::string &cfg, std::vector< std::string > &files, const bool writeLocations)
simplecpp::TokenList preprocess(const simplecpp::TokenList &tokens1, const std::string &cfg, std::vector< std::string > &files, bool throwError=false)
static char macroChar
character that is inserted in expanded macros
Definition: preprocessor.h:88
void dump(std::ostream &out) const
dump all directives present in source file
void inlineSuppressions(const simplecpp::TokenList &tokens, SuppressionList &suppressions)
std::list< Directive > createDirectives(const simplecpp::TokenList &tokens) const
This is just a container for general settings so that we don't need to pass individual values to func...
Definition: settings.h:95
std::set< std::string > userUndefs
undefines given by the user
Definition: settings.h:389
bool quiet
Is –quiet given?
Definition: settings.h:282
int pid
pid of cppcheck.
Definition: settings.h:267
bool preprocessOnly
Using -E for debugging purposes.
Definition: settings.h:279
bool checkAllConfigurations
check all configurations (false if -D or –max-configs is used
Definition: settings.h:124
std::string getMisraRuleText(const std::string &id, const std::string &text) const
Definition: settings.cpp:608
std::vector< std::string > basePaths
Paths used as base for conversion to relative paths.
Definition: settings.h:118
int maxCtuDepth
–max-ctu-depth
Definition: settings.h:247
Suppressions supprs
suppressions
Definition: settings.h:369
std::string plistOutput
plist output (–plist-output=<dir>)
Definition: settings.h:270
std::string clangExecutable
Custom Clang executable.
Definition: settings.h:153
Standards::Language enforcedLang
Name of the language that is enforced.
Definition: settings.h:190
SimpleEnableGroup< Checks > checks
Definition: settings.h:360
bool checkConfiguration
Is the 'configuration checking' wanted?
Definition: settings.h:127
static bool terminated()
termination requested?
Definition: settings.h:449
bool relativePaths
Use relative paths in output.
Definition: settings.h:285
std::unordered_set< std::string > addons
addons, either filename of python/json file or json data
Definition: settings.h:109
Library library
Library.
Definition: settings.h:237
bool clang
Use Clang.
Definition: settings.h:150
bool safety
Safety certified behavior Show checkers report when Cppcheck finishes Make cppcheck checking more str...
Definition: settings.h:313
std::string buildDir
–cppcheck-build-dir.
Definition: settings.h:121
std::string userDefines
defines given by the user
Definition: settings.h:386
std::vector< AddonInfo > addonInfos
the loaded addons infos
Definition: settings.h:112
std::string premiumArgs
Extra arguments for Cppcheck Premium addon.
Definition: settings.h:273
Platform platform
Definition: settings.h:255
bool dump
Is –dump given?
Definition: settings.h:186
int checksMaxTime
The maximum time in seconds for the checks of a single file.
Definition: settings.h:138
bool useSingleJob() const
Definition: settings.h:457
bool debugnormal
Is –debug-normal given?
Definition: settings.h:174
bool force
Force checking the files with "too many" configurations (–force).
Definition: settings.h:220
bool verbose
Is –verbose given?
Definition: settings.h:398
std::list< std::string > includePaths
List of include paths, e.g.
Definition: settings.h:224
std::string dumpFile
Definition: settings.h:187
SHOWTIME_MODES showtime
show timing information (–showtime=file|summary|top5)
Definition: settings.h:363
int maxConfigs
Maximum number of configurations to check before bailing.
Definition: settings.h:244
SimpleEnableGroup< Severity > severity
Definition: settings.h:358
std::string addonPython
Path to the python interpreter to be used to run addons.
Definition: settings.h:115
bool debugwarnings
Is –debug-warnings given?
Definition: settings.h:183
Standards standards
Struct contains standards settings.
Definition: settings.h:366
bool isEnabled(T flag) const
Definition: settings.h:66
void enable(T flag)
Definition: settings.h:69
void dump(std::ostream &out) const
Create an xml dump of suppressions.
void markUnmatchedInlineSuppressionsAsChecked(const Tokenizer &tokenizer)
Marks Inline Suppressions as checked if source line is in the token stream.
const std::list< Suppression > & getSuppressions() const
Returns list of all suppressions.
std::string addSuppressions(std::list< Suppression > suppressions)
Combine list of suppressions into the current suppressions.
bool isSuppressed(const ErrorMessage &errmsg, bool global=true)
Returns true if this message should not be shown to the user.
bool isSuppressedExplicitly(const ErrorMessage &errmsg, bool global=true)
Returns true if this message is "explicitly" suppressed.
static bool reportUnmatchedSuppressions(const std::list< SuppressionList::Suppression > &unmatched, ErrorLogger &errorLogger)
Report unmatched suppressions.
std::list< Suppression > getUnmatchedLocalSuppressions(const std::string &file, const bool unusedFunctionChecking) const
Returns list of unmatched local (per-file) suppressions.
void showResults(SHOWTIME_MODES mode) const
Definition: timer.cpp:41
void reset()
Definition: timer.cpp:99
Definition: timer.h:70
void stop()
Definition: timer.cpp:123
const std::string & getSourceFilePath() const
Definition: tokenlist.cpp:75
std::size_t calculateHash() const
Calculates a hash of the token list used to compare multiple token lists with each other as quickly a...
Definition: tokenlist.cpp:424
void setLang(Standards::Language lang)
const std::vector< std::string > & getFiles() const
Get filenames (the sourcefile + the files it include).
Definition: tokenlist.h:141
const Token * front() const
get first token of list
Definition: tokenlist.h:119
bool createTokens(std::istream &code, const std::string &file0)
Create tokens from code.
Definition: tokenlist.cpp:336
int appendFileIfNew(std::string fileName)
append file name if seen the first time; return its index in any case
Definition: tokenlist.cpp:110
The token list that the TokenList generates is a linked-list of this class.
Definition: token.h:150
Token * next()
Definition: token.h:830
The main purpose is to tokenize the source code.
Definition: tokenize.h:46
void printDebugOutput(int simplification) const
print –debug output if debug flags match the simplification: 0=unknown/both simplifications 1=1st sim...
Definition: tokenize.cpp:5875
const Token * tokens() const
Definition: tokenize.h:592
bool simplifyTokens1(const std::string &configuration)
Definition: tokenize.cpp:3379
void setDirectives(std::list< Directive > directives)
TokenList list
Token list: stores all tokens.
Definition: tokenize.h:590
const SymbolDatabase * getSymbolDatabase() const
Definition: tokenize.h:563
void dump(std::ostream &out) const
Definition: tokenize.cpp:5925
void setTimerResults(TimerResults *tr)
Definition: tokenize.h:59
Color
Definition: color.h:27
static const std::string emptyString
Definition: config.h:127
static constexpr char FILELIST[]
Definition: cppcheck.cpp:81
static std::string cmdFileName(std::string f)
Definition: cppcheck.cpp:105
static const CWE CWE398(398U)
static constexpr char ExtraVersion[]
Definition: cppcheck.cpp:79
static std::string detectPython(const CppCheck::ExecuteCmdFn &executeCommand)
Definition: cppcheck.cpp:207
static std::vector< std::string > split(const std::string &str, const std::string &sep=" ")
Definition: cppcheck.cpp:113
static std::string getDumpFileName(const Settings &settings, const std::string &filename)
Definition: cppcheck.cpp:136
static std::vector< picojson::value > executeAddon(const AddonInfo &addonInfo, const std::string &defaultPythonExe, const std::string &file, const std::string &premiumArgs, const CppCheck::ExecuteCmdFn &executeCommand)
Definition: cppcheck.cpp:231
static constexpr char Version[]
Definition: cppcheck.cpp:78
static std::string getDefinesFlags(const std::string &semicolonSeparatedString)
Definition: cppcheck.cpp:325
static bool reportClangErrors(std::istream &is, const std::function< void(const ErrorMessage &)> &reportErr, std::vector< ErrorMessage > &warnings)
Definition: cppcheck.cpp:364
static TimerResults s_timerResults
Definition: cppcheck.cpp:83
static void createDumpFile(const Settings &settings, const std::string &filename, std::ofstream &fdump, std::string &dumpFile)
Definition: cppcheck.cpp:157
static simplecpp::TokenList createTokenList(const std::string &filename, std::vector< std::string > &files, simplecpp::OutputList *outputList, std::istream *fileStream)
Definition: cppcheck.cpp:593
static std::string getCtuInfoFileName(const std::string &dumpFile)
Definition: cppcheck.cpp:152
Severity severityFromString(const std::string &severity)
Definition: errortypes.cpp:76
@ none
No severity (default value).
@ warning
Warning.
@ portability
Portability warning.
@ style
Style warning.
@ debug
Debug message.
@ information
Checking information.
@ performance
Performance warning.
@ error
Programming error.
@ internal
Internal message.
@ unusedFunction
int maxCtuDepth
Definition: ctu.cpp:58
CPPCHECKLIB FileInfo * getFileInfo(const Tokenizer &tokenizer)
Parse current TU and extract file info.
Definition: ctu.cpp:309
void setValues(TokenList &tokenlist, SymbolDatabase &symboldatabase, ErrorLogger &errorLogger, const Settings &settings, TimerResultsIntf *timerResults)
Perform valueflow analysis.
Definition: valueflow.cpp:9604
void CPPCHECKLIB parseClangAstDump(Tokenizer &tokenizer, std::istream &f)
static std::string cfg(const std::vector< std::string > &configs, const std::string &userDefines)
std::string args
Definition: addoninfo.h:30
std::string scriptFile
Definition: addoninfo.h:28
std::string name
Definition: addoninfo.h:27
bool ctu
Definition: addoninfo.h:32
std::string python
Definition: addoninfo.h:31
std::string runScript
Definition: addoninfo.h:33
std::string executable
Definition: addoninfo.h:29
A preprocessor directive Each preprocessor directive (#include, #define, #undef, #if,...
Definition: preprocessor.h:49
File settings.
Definition: filesettings.h:57
std::string cppcheckDefines() const
Definition: filesettings.h:74
std::string defines
Definition: filesettings.h:72
const std::string & filename() const
Definition: filesettings.h:68
std::set< std::string > undefs
Definition: filesettings.h:77
std::string cfg
Definition: filesettings.h:66
Platform::Type platformType
Definition: filesettings.h:82
std::string standard
Definition: filesettings.h:81
std::list< std::string > includePaths
Definition: filesettings.h:78
std::list< std::string > systemIncludePaths
Definition: filesettings.h:80
Simple container to be thrown when internal error is detected.
Definition: errortypes.h:36
bool setC(const std::string &str)
Definition: standards.h:48
std::string getC() const
Definition: standards.h:64
std::string stdValue
–std value given on command line
Definition: standards.h:46
bool setCPP(std::string str)
Definition: standards.h:97
std::string getCPP() const
Definition: standards.h:103
static SuppressionList::ErrorMessage fromErrorMessage(const ::ErrorMessage &msg, const std::set< std::string > &macroNames)
SuppressionList nofail
suppress exitcode
Definition: suppressions.h:266
SuppressionList nomsg
suppress message (–suppressions)
Definition: suppressions.h:264
SHOWTIME_MODES
Definition: timer.h:30
bool startsWith(const std::string &str, const char start[], std::size_t startlen)
Definition: utils.h:94
bool endsWith(const std::string &str, char c)
Definition: utils.h:110
#define CPPCHECK_VERSION_STRING
Definition: version.h:18