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