59 #include <unordered_set>
71 #include <simplecpp.h>
87 static constexpr
char FILELIST[] =
"cppcheck-addon-ctu-file-list";
98 FilesDeleter() =
default;
100 for (
const std::string& fileName: mFilenames)
101 std::remove(fileName.c_str());
103 void addFile(
const std::string& fileName) {
104 mFilenames.push_back(fileName);
107 std::vector<std::string> mFilenames;
114 if (f.find(
' ') != std::string::npos)
115 return "\"" + f +
"\"";
119 static std::vector<std::string>
split(
const std::string &str,
const std::string &sep=
" ")
121 std::vector<std::string> ret;
122 for (std::string::size_type startPos = 0U; startPos < str.size();) {
123 startPos = str.find_first_not_of(sep, startPos);
124 if (startPos == std::string::npos)
127 if (str[startPos] ==
'\"') {
128 const std::string::size_type endPos = str.find(
'\"', startPos + 1);
129 ret.push_back(str.substr(startPos + 1, endPos - startPos - 1));
130 startPos = (endPos < str.size()) ? (endPos + 1) : endPos;
134 const std::string::size_type endPos = str.find(sep, startPos + 1);
135 ret.push_back(str.substr(startPos, endPos - startPos));
156 std::string extension;
160 extension =
"." + std::to_string(
getPid()) +
".dump";
164 return filename + extension;
169 return dumpFile.substr(0, dumpFile.size()-4) +
"ctu-info";
173 const std::string& filename,
174 std::ofstream& fdump,
175 std::string& dumpFile)
177 if (!settings.
dump && settings.
addons.empty())
181 fdump.open(dumpFile);
182 if (!fdump.is_open())
189 std::string language;
191 case Standards::Language::C:
192 language =
" language=\"c\"";
194 case Standards::Language::CPP:
195 language =
" language=\"cpp\"";
197 case Standards::Language::None:
201 if (lang == Standards::Language::CPP)
202 language =
" language=\"cpp\"";
203 else if (lang == Standards::Language::C)
204 language =
" language=\"c\"";
209 fdump <<
"<?xml version=\"1.0\"?>\n";
210 fdump <<
"<dumps" << language <<
">\n";
211 fdump <<
" <platform"
225 const char *py_exes[] = {
"python3.exe",
"python.exe" };
227 const char *py_exes[] = {
"python3",
"python" };
229 for (
const char* py_exe : py_exes) {
233 const std::string cmd = std::string(py_exe) +
" --version >NUL 2>&1";
234 if (system(cmd.c_str()) != 0) {
239 if (executeCommand(py_exe,
split(
"--version"),
"2>&1", out) == EXIT_SUCCESS &&
startsWith(out,
"Python ") && std::isdigit(out[7])) {
247 const std::string &defaultPythonExe,
248 const std::string &file,
249 const std::string &premiumArgs,
252 const std::string redirect =
"2>&1";
254 std::string pythonExe;
258 else if (!addonInfo.
python.empty())
260 else if (!defaultPythonExe.empty())
264 static const std::string detectedPythonExe =
detectPython(executeCommand);
265 if (detectedPythonExe.empty())
266 throw InternalError(
nullptr,
"Failed to auto detect python");
267 pythonExe = detectedPythonExe;
273 args += std::string(args.empty() ?
"" :
" ") +
"--cli" + addonInfo.
args;
274 if (!premiumArgs.empty() && !addonInfo.
executable.empty())
275 args +=
" " + premiumArgs;
277 const bool is_file_list = (file.find(
FILELIST) != std::string::npos);
278 const std::string fileArg = (is_file_list ?
" --file-list " :
" ") +
cmdFileName(file);
282 if (
const int exitcode = executeCommand(pythonExe,
split(args), redirect, result)) {
283 std::string message(
"Failed to execute addon '" + addonInfo.
name +
"' - exitcode is " + std::to_string(exitcode));
284 std::string details = pythonExe +
" " + args;
285 if (result.size() > 2) {
286 details +=
"\nOutput:\n";
288 const auto pos = details.find_last_not_of(
"\n\r");
289 if (pos != std::string::npos)
290 details.resize(pos + 1);
292 throw InternalError(
nullptr, std::move(message), std::move(details));
295 std::vector<picojson::value> addonResult;
298 std::istringstream istr(result);
300 while (std::getline(istr, line)) {
313 if (line[0] !=
'{') {
316 result.erase(result.find_last_not_of(
'\n') + 1, std::string::npos);
317 throw InternalError(
nullptr,
"Failed to execute '" + pythonExe +
" " + args +
"'. " + result);
324 const std::string err = picojson::parse(res, line);
329 if (!res.is<picojson::object>()) {
333 addonResult.emplace_back(std::move(res));
343 for (
const std::string &d:
split(semicolonSeparatedString,
";"))
344 flags +=
"-D" + d +
" ";
349 bool useGlobalSuppressions,
351 : mErrorLogger(errorLogger)
352 , mUseGlobalSuppressions(useGlobalSuppressions)
353 , mExecuteCommand(std::move(executeCommand))
382 while (std::getline(is, line)) {
383 if (line.empty() || line[0] ==
' ' || line[0] ==
'`' || line[0] ==
'-')
386 std::string::size_type pos3 = line.find(
": error: ");
387 if (pos3 == std::string::npos)
388 pos3 = line.find(
": fatal error:");
389 if (pos3 == std::string::npos)
390 pos3 = line.find(
": warning:");
391 if (pos3 == std::string::npos)
395 const std::string::size_type pos2 = line.rfind(
':', pos3 - 1);
396 const std::string::size_type pos1 = line.rfind(
':', pos2 - 1);
398 if (pos1 >= pos2 || pos2 >= pos3)
401 const std::string filename = line.substr(0, pos1);
402 const std::string linenr = line.substr(pos1+1, pos2-pos1-1);
403 const std::string colnr = line.substr(pos2+1, pos3-pos2-1);
404 const std::string msg = line.substr(line.find(
':', pos3+1) + 2);
407 const int line_i = strToInt<int>(linenr);
408 const int column = strToInt<unsigned int>(colnr);
417 if (line.compare(pos3, 10,
": warning:") == 0) {
418 warnings.push_back(std::move(errmsg));
438 const bool isCpp =
Path::identify(path) == Standards::Language::CPP;
439 const std::string langOpt = isCpp ?
"-x c++" :
"-x c";
441 const std::string clangcmd = analyzerInfo +
".clang-cmd";
442 const std::string clangStderr = analyzerInfo +
".clang-stderr";
443 const std::string clangAst = analyzerInfo +
".clang-ast";
452 std::string flags(langOpt +
" ");
458 flags +=
"-I" + i +
" ";
462 const std::string args2 =
"-fsyntax-only -Xclang -ast-dump -fno-color-diagnostics " + flags + path;
463 const std::string redirect2 = analyzerInfo.empty() ? std::string(
"2>&1") : (
"2> " + clangStderr);
465 std::ofstream fout(clangcmd);
466 fout << exe <<
" " << args2 <<
" " << redirect2 << std::endl;
473 if (exitcode != EXIT_SUCCESS) {
475 std::cerr <<
"Failed to execute '" << exe <<
" " << args2 <<
" " << redirect2 <<
"' - (exitcode: " << exitcode <<
" / output: " << output2 <<
")" << std::endl;
479 if (output2.find(
"TranslationUnitDecl") == std::string::npos) {
481 std::cerr <<
"Failed to execute '" << exe <<
" " << args2 <<
" " << redirect2 <<
"' - (no TranslationUnitDecl in output)" << std::endl;
486 std::vector<ErrorMessage> compilerWarnings;
488 std::ifstream fin(clangStderr);
489 auto reportError = [
this](
const ErrorMessage& errorMessage) {
495 std::istringstream istr(output2);
496 auto reportError = [
this](
const ErrorMessage& errorMessage) {
504 std::ofstream fout(clangAst);
505 fout << output2 << std::endl;
512 std::istringstream ast(output2);
525 std::string dumpFile;
527 if (fdump.is_open()) {
529 fdump <<
"<dump cfg=\"\">\n";
531 fdump <<
" <clang-warning file=\"" <<
toxml(errmsg.callStack.front().getfile()) <<
"\" line=\"" << errmsg.callStack.front().line <<
"\" column=\"" << errmsg.callStack.front().column <<
"\" message=\"" <<
toxml(errmsg.shortMessage()) <<
"\"/>\n";
532 fdump <<
" <standards>\n";
535 fdump <<
" </standards>\n";
536 tokenizer.
dump(fdump);
537 fdump <<
"</dump>\n";
538 fdump <<
"</dumps>\n";
551 }
catch (
const std::exception &e) {
552 internalError(path, std::string(
"Processing Clang AST dump failed: ") + e.what());
568 std::istringstream iss(content);
588 if (fs.
standard.find(
"++") != std::string::npos)
609 static simplecpp::TokenList
createTokenList(
const std::string& filename, std::vector<std::string>& files, simplecpp::OutputList* outputList, std::istream* fileStream)
612 return {*fileStream, files, filename, outputList};
614 return {filename, files, outputList};
617 unsigned int CppCheck::checkFile(
const std::string& filename,
const std::string &cfgname, std::istream* fileStream)
644 std::string includePaths;
646 includePaths +=
" -I" + I;
667 std::ifstream in(filename);
675 simplecpp::OutputList outputList;
676 std::vector<std::string> files;
677 simplecpp::TokenList tokens1 =
createTokenList(filename, files, &outputList, fileStream);
680 const auto output_it = std::find_if(outputList.cbegin(), outputList.cend(), [](
const simplecpp::Output &output){
681 return Preprocessor::hasErrors(output);
683 if (output_it != outputList.cend()) {
684 const simplecpp::Output &output = *output_it;
703 if (!preprocessor.
loadFiles(tokens1, files))
707 std::string filename2;
708 if (filename.find(
'/') != std::string::npos)
709 filename2 = filename.substr(filename.rfind(
'/') + 1);
711 filename2 = filename;
712 const std::size_t fileNameHash = std::hash<std::string> {}(filename);
713 filename2 =
mSettings.
plistOutput + filename2.substr(0, filename2.find(
'.')) +
"_" + std::to_string(fileNameHash) +
".plist";
718 std::string dumpProlog;
720 dumpProlog +=
" <rawtokens>\n";
721 for (
unsigned int i = 0; i < files.size(); ++i) {
722 dumpProlog +=
" <file index=\"";
723 dumpProlog += std::to_string(i);
724 dumpProlog +=
"\" name=\"";
726 dumpProlog +=
"\"/>\n";
728 for (
const simplecpp::Token *tok = tokens1.cfront(); tok; tok = tok->next) {
729 dumpProlog +=
" <tok ";
731 dumpProlog +=
"fileIndex=\"";
732 dumpProlog += std::to_string(tok->location.fileIndex);
735 dumpProlog +=
"linenr=\"";
736 dumpProlog += std::to_string(tok->location.line);
739 dumpProlog +=
"column=\"";
740 dumpProlog += std::to_string(tok->location.col);
743 dumpProlog +=
"str=\"";
747 dumpProlog +=
"/>\n";
749 dumpProlog +=
" </rawtokens>\n";
755 std::ostringstream oss;
757 dumpProlog += oss.str();
759 tokens1.removeComments();
764 std::ostringstream toolinfo;
775 const std::size_t hash = preprocessor.
calculateHash(tokens1, toolinfo.str());
776 std::list<ErrorMessage> errors;
778 while (!errors.empty()) {
786 FilesDeleter filesDeleter;
790 std::string dumpFile;
792 if (fdump.is_open()) {
795 filesDeleter.addFile(dumpFile);
805 std::set<std::string> configurations;
808 configurations = preprocessor.
getConfigs(tokens1);
814 for (
const std::string &config : configurations)
815 (void)preprocessor.
getcode(tokens1, config, files,
true);
822 const auto rules_it = std::find_if(
mSettings.rules.cbegin(),
mSettings.rules.cend(), [](
const Settings::Rule& rule) {
823 return rule.tokenlist ==
"define";
825 if (rules_it !=
mSettings.rules.cend()) {
827 const std::list<Directive> &directives = preprocessor.
getDirectives();
828 for (
const Directive &dir : directives) {
830 code +=
"#line " + std::to_string(dir.linenr) +
" \"" + dir.file +
"\"\n" + dir.str +
'\n';
833 std::istringstream istr2(code);
835 executeRules(
"define", tokenizer2);
847 std::set<unsigned long long> hashes;
849 bool hasValidConfig =
false;
850 std::list<std::string> configurationError;
851 for (
const std::string &currCfg : configurations) {
864 for (
const std::string &
cfg:
split(currCfg,
";")) {
865 if (std::find(v1.cbegin(), v1.cend(),
cfg) == v1.cend()) {
879 codeWithoutCfg.insert(0U,
"//");
880 std::string::size_type pos = 0;
881 while ((pos = codeWithoutCfg.find(
"\n#file",pos)) != std::string::npos)
882 codeWithoutCfg.insert(pos+1U,
"//");
884 while ((pos = codeWithoutCfg.find(
"\n#endfile",pos)) != std::string::npos)
885 codeWithoutCfg.insert(pos+1U,
"//");
888 codeWithoutCfg[pos] =
' ';
904 hasValidConfig =
true;
909 if (!tok->getMacroName().empty())
937 fdump <<
" <standards>" << std::endl;
940 fdump <<
" </standards>" << std::endl;
941 preprocessor.
dump(fdump);
942 tokenizer.
dump(fdump);
943 fdump <<
"</dump>" << std::endl;
952 if (hashes.find(hash) != hashes.end()) {
965 if (hasRule(
"simple"))
966 throw InternalError(
nullptr,
"Handling of \"simple\" rules has been removed in Cppcheck. Use --addon instead.");
969 }
catch (
const simplecpp::Output &o) {
971 configurationError.push_back((
mCurrentConfig.empty() ?
"\'\'" :
mCurrentConfig) +
" : [" + o.location.file() +
':' + std::to_string(o.location.line) +
"] " + o.msg);
974 if (!hasValidConfig && currCfg == *configurations.rbegin()) {
986 "preprocessorErrorDirective",
1004 msg =
"This file is not analyzed. Cppcheck failed to extract a valid configuration. Use -v for more details.";
1005 msg +=
"\nThis file is not analyzed. Cppcheck failed to extract a valid configuration. The tested configurations have these preprocessor errors:";
1006 for (
const std::string &s : configurationError)
1015 "noValidConfiguration",
1021 if (fdump.is_open()) {
1022 fdump <<
"</dumps>" << std::endl;
1031 }
catch (
const std::runtime_error &e) {
1032 internalError(filename, std::string(
"Checking file failed: ") + e.what());
1033 }
catch (
const std::bad_alloc &) {
1034 internalError(filename,
"Checking file failed: out of memory");
1061 const std::string fullmsg(
"Bailing out from analysis: " + msg);
1082 executeRules(
"raw", tokenizer);
1097 const char* unusedFunctionOnly = std::getenv(
"UNUSEDFUNCTION_ONLY");
1098 const bool doUnusedFunctionOnly = unusedFunctionOnly && (std::strcmp(unusedFunctionOnly,
"1") == 0);
1100 if (!doUnusedFunctionOnly) {
1109 if (maxTime > 0 && std::time(
nullptr) > maxTime) {
1115 "Checks maximum time exceeded",
1124 check->runChecks(tokenizer,
this);
1152 if (!doUnusedFunctionOnly) {
1172 executeRules(
"normal", tokenizer);
1179 bool CppCheck::hasRule(
const std::string &tokenlist)
const
1181 return std::any_of(
mSettings.rules.cbegin(),
mSettings.rules.cend(), [&](
const Settings::Rule& rule) {
1182 return rule.tokenlist == tokenlist;
1186 static const char * pcreErrorCodeToString(
const int pcreExecRet)
1188 switch (pcreExecRet) {
1189 case PCRE_ERROR_NULL:
1190 return "Either code or subject was passed as NULL, or ovector was NULL "
1191 "and ovecsize was not zero (PCRE_ERROR_NULL)";
1192 case PCRE_ERROR_BADOPTION:
1193 return "An unrecognized bit was set in the options argument (PCRE_ERROR_BADOPTION)";
1194 case PCRE_ERROR_BADMAGIC:
1195 return "PCRE stores a 4-byte \"magic number\" at the start of the compiled code, "
1196 "to catch the case when it is passed a junk pointer and to detect when a "
1197 "pattern that was compiled in an environment of one endianness is run in "
1198 "an environment with the other endianness. This is the error that PCRE "
1199 "gives when the magic number is not present (PCRE_ERROR_BADMAGIC)";
1200 case PCRE_ERROR_UNKNOWN_NODE:
1201 return "While running the pattern match, an unknown item was encountered in the "
1202 "compiled pattern. This error could be caused by a bug in PCRE or by "
1203 "overwriting of the compiled pattern (PCRE_ERROR_UNKNOWN_NODE)";
1204 case PCRE_ERROR_NOMEMORY:
1205 return "If a pattern contains back references, but the ovector that is passed "
1206 "to pcre_exec() is not big enough to remember the referenced substrings, "
1207 "PCRE gets a block of memory at the start of matching to use for this purpose. "
1208 "If the call via pcre_malloc() fails, this error is given. The memory is "
1209 "automatically freed at the end of matching. This error is also given if "
1210 "pcre_stack_malloc() fails in pcre_exec(). "
1211 "This can happen only when PCRE has been compiled with "
1212 "--disable-stack-for-recursion (PCRE_ERROR_NOMEMORY)";
1213 case PCRE_ERROR_NOSUBSTRING:
1214 return "This error is used by the pcre_copy_substring(), pcre_get_substring(), "
1215 "and pcre_get_substring_list() functions (see below). "
1216 "It is never returned by pcre_exec() (PCRE_ERROR_NOSUBSTRING)";
1217 case PCRE_ERROR_MATCHLIMIT:
1218 return "The backtracking limit, as specified by the match_limit field in a pcre_extra "
1219 "structure (or defaulted) was reached. "
1220 "See the description above (PCRE_ERROR_MATCHLIMIT)";
1221 case PCRE_ERROR_CALLOUT:
1222 return "This error is never generated by pcre_exec() itself. "
1223 "It is provided for use by callout functions that want to yield a distinctive "
1224 "error code. See the pcrecallout documentation for details (PCRE_ERROR_CALLOUT)";
1225 case PCRE_ERROR_BADUTF8:
1226 return "A string that contains an invalid UTF-8 byte sequence was passed as a subject, "
1227 "and the PCRE_NO_UTF8_CHECK option was not set. If the size of the output vector "
1228 "(ovecsize) is at least 2, the byte offset to the start of the the invalid UTF-8 "
1229 "character is placed in the first element, and a reason code is placed in the "
1230 "second element. The reason codes are listed in the following section. For "
1231 "backward compatibility, if PCRE_PARTIAL_HARD is set and the problem is a truncated "
1232 "UTF-8 character at the end of the subject (reason codes 1 to 5), "
1233 "PCRE_ERROR_SHORTUTF8 is returned instead of PCRE_ERROR_BADUTF8";
1234 case PCRE_ERROR_BADUTF8_OFFSET:
1235 return "The UTF-8 byte sequence that was passed as a subject was checked and found to "
1236 "be valid (the PCRE_NO_UTF8_CHECK option was not set), but the value of "
1237 "startoffset did not point to the beginning of a UTF-8 character or the end of "
1238 "the subject (PCRE_ERROR_BADUTF8_OFFSET)";
1239 case PCRE_ERROR_PARTIAL:
1240 return "The subject string did not match, but it did match partially. See the "
1241 "pcrepartial documentation for details of partial matching (PCRE_ERROR_PARTIAL)";
1242 case PCRE_ERROR_BADPARTIAL:
1243 return "This code is no longer in use. It was formerly returned when the PCRE_PARTIAL "
1244 "option was used with a compiled pattern containing items that were not supported "
1245 "for partial matching. From release 8.00 onwards, there are no restrictions on "
1246 "partial matching (PCRE_ERROR_BADPARTIAL)";
1247 case PCRE_ERROR_INTERNAL:
1248 return "An unexpected internal error has occurred. This error could be caused by a bug "
1249 "in PCRE or by overwriting of the compiled pattern (PCRE_ERROR_INTERNAL)";
1250 case PCRE_ERROR_BADCOUNT:
1251 return "This error is given if the value of the ovecsize argument is negative "
1252 "(PCRE_ERROR_BADCOUNT)";
1253 case PCRE_ERROR_RECURSIONLIMIT:
1254 return "The internal recursion limit, as specified by the match_limit_recursion "
1255 "field in a pcre_extra structure (or defaulted) was reached. "
1256 "See the description above (PCRE_ERROR_RECURSIONLIMIT)";
1257 case PCRE_ERROR_DFA_UITEM:
1258 return "PCRE_ERROR_DFA_UITEM";
1259 case PCRE_ERROR_DFA_UCOND:
1260 return "PCRE_ERROR_DFA_UCOND";
1261 case PCRE_ERROR_DFA_WSSIZE:
1262 return "PCRE_ERROR_DFA_WSSIZE";
1263 case PCRE_ERROR_DFA_RECURSE:
1264 return "PCRE_ERROR_DFA_RECURSE";
1265 case PCRE_ERROR_NULLWSLIMIT:
1266 return "PCRE_ERROR_NULLWSLIMIT";
1267 case PCRE_ERROR_BADNEWLINE:
1268 return "An invalid combination of PCRE_NEWLINE_xxx options was "
1269 "given (PCRE_ERROR_BADNEWLINE)";
1270 case PCRE_ERROR_BADOFFSET:
1271 return "The value of startoffset was negative or greater than the length "
1272 "of the subject, that is, the value in length (PCRE_ERROR_BADOFFSET)";
1273 case PCRE_ERROR_SHORTUTF8:
1274 return "This error is returned instead of PCRE_ERROR_BADUTF8 when the subject "
1275 "string ends with a truncated UTF-8 character and the PCRE_PARTIAL_HARD option is set. "
1276 "Information about the failure is returned as for PCRE_ERROR_BADUTF8. "
1277 "It is in fact sufficient to detect this case, but this special error code for "
1278 "PCRE_PARTIAL_HARD precedes the implementation of returned information; "
1279 "it is retained for backwards compatibility (PCRE_ERROR_SHORTUTF8)";
1280 case PCRE_ERROR_RECURSELOOP:
1281 return "This error is returned when pcre_exec() detects a recursion loop "
1282 "within the pattern. Specifically, it means that either the whole pattern "
1283 "or a subpattern has been called recursively for the second time at the same "
1284 "position in the subject string. Some simple patterns that might do this "
1285 "are detected and faulted at compile time, but more complicated cases, "
1286 "in particular mutual recursions between two different subpatterns, "
1287 "cannot be detected until run time (PCRE_ERROR_RECURSELOOP)";
1288 case PCRE_ERROR_JIT_STACKLIMIT:
1289 return "This error is returned when a pattern that was successfully studied "
1290 "using a JIT compile option is being matched, but the memory available "
1291 "for the just-in-time processing stack is not large enough. See the pcrejit "
1292 "documentation for more details (PCRE_ERROR_JIT_STACKLIMIT)";
1293 case PCRE_ERROR_BADMODE:
1294 return "This error is given if a pattern that was compiled by the 8-bit library "
1295 "is passed to a 16-bit or 32-bit library function, or vice versa (PCRE_ERROR_BADMODE)";
1296 case PCRE_ERROR_BADENDIANNESS:
1297 return "This error is given if a pattern that was compiled and saved is reloaded on a "
1298 "host with different endianness. The utility function pcre_pattern_to_host_byte_order() "
1299 "can be used to convert such a pattern so that it runs on the new host (PCRE_ERROR_BADENDIANNESS)";
1300 case PCRE_ERROR_DFA_BADRESTART:
1301 return "PCRE_ERROR_DFA_BADRESTART";
1302 #if PCRE_MAJOR >= 8 && PCRE_MINOR >= 32
1303 case PCRE_ERROR_BADLENGTH:
1304 return "This error is given if pcre_exec() is called with a negative value for the length argument (PCRE_ERROR_BADLENGTH)";
1305 case PCRE_ERROR_JIT_BADOPTION:
1306 return "This error is returned when a pattern that was successfully studied using a JIT compile "
1307 "option is being matched, but the matching mode (partial or complete match) does not correspond "
1308 "to any JIT compilation mode. When the JIT fast path function is used, this error may be "
1309 "also given for invalid options. See the pcrejit documentation for more details (PCRE_ERROR_JIT_BADOPTION)";
1315 void CppCheck::executeRules(
const std::string &tokenlist,
const Tokenizer &tokenizer)
1318 if (!hasRule(tokenlist))
1322 std::ostringstream ostr;
1324 ostr <<
" " << tok->str();
1325 const std::string str(ostr.str());
1327 for (
const Settings::Rule &rule :
mSettings.rules) {
1328 if (rule.pattern.empty() || rule.id.empty() || rule.severity ==
Severity::none || rule.tokenlist != tokenlist)
1335 const char *pcreCompileErrorStr =
nullptr;
1337 pcre *
const re = pcre_compile(rule.pattern.c_str(),0,&pcreCompileErrorStr,&erroffset,
nullptr);
1339 if (pcreCompileErrorStr) {
1340 const std::string msg =
"pcre_compile failed: " + std::string(pcreCompileErrorStr);
1341 const ErrorMessage errmsg(std::list<ErrorMessage::FileLocation>(),
1354 #ifdef PCRE_CONFIG_JIT
1355 const char *pcreStudyErrorStr =
nullptr;
1356 pcre_extra *
const pcreExtra = pcre_study(re, PCRE_STUDY_JIT_COMPILE, &pcreStudyErrorStr);
1360 if (pcreStudyErrorStr) {
1361 const std::string msg =
"pcre_study failed: " + std::string(pcreStudyErrorStr);
1362 const ErrorMessage errmsg(std::list<ErrorMessage::FileLocation>(),
1375 const pcre_extra *
const pcreExtra =
nullptr;
1379 int ovector[30]= {0};
1380 while (pos < (
int)str.size()) {
1381 const int pcreExecRet = pcre_exec(re, pcreExtra, str.c_str(), (
int)str.size(), pos, 0, ovector, 30);
1382 if (pcreExecRet < 0) {
1383 const std::string errorMessage = pcreErrorCodeToString(pcreExecRet);
1384 if (!errorMessage.empty()) {
1385 const ErrorMessage errmsg(std::list<ErrorMessage::FileLocation>(),
1388 std::string(
"pcre_exec failed: ") + errorMessage,
1396 const auto pos1 = (
unsigned int)ovector[0];
1397 const auto pos2 = (
unsigned int)ovector[1];
1406 std::size_t len = 0;
1408 len = len + 1U + tok->str().size();
1411 line = tok->linenr();
1419 std::string summary;
1420 if (rule.summary.empty())
1421 summary =
"found '" + str.substr(pos1, pos2 - pos1) +
"'";
1423 summary = rule.summary;
1431 #ifdef PCRE_CONFIG_JIT
1434 pcre_free_study(pcreExtra);
1443 if (!dumpFile.empty()) {
1444 std::vector<std::string> f{dumpFile};
1454 FilesDeleter filesDeleter;
1456 std::string fileList;
1458 if (files.size() >= 2 ||
endsWith(files[0],
".ctu-info")) {
1460 filesDeleter.addFile(fileList);
1461 std::ofstream fout(fileList);
1462 for (
const std::string& f: files)
1463 fout << f << std::endl;
1470 if (addonInfo.
name !=
"misra" && !addonInfo.
ctu &&
endsWith(files.back(),
".ctu-info"))
1473 const std::vector<picojson::value> results =
1478 for (
const picojson::value& res : results) {
1481 picojson::object obj = res.get<picojson::object>();
1485 if (obj.count(
"file") > 0) {
1486 std::string fileName = obj[
"file"].get<std::string>();
1487 const int64_t lineNumber = obj[
"linenr"].get<int64_t>();
1488 const int64_t column = obj[
"column"].get<int64_t>();
1489 errmsg.
callStack.emplace_back(std::move(fileName), lineNumber, column);
1490 }
else if (obj.count(
"loc") > 0) {
1491 for (
const picojson::value &locvalue: obj[
"loc"].get<picojson::array>()) {
1492 picojson::object loc = locvalue.get<picojson::object>();
1493 std::string fileName = loc[
"file"].get<std::string>();
1494 const int64_t lineNumber = loc[
"linenr"].get<int64_t>();
1495 const int64_t column = loc[
"column"].get<int64_t>();
1496 const std::string info = loc[
"info"].get<std::string>();
1497 errmsg.callStack.emplace_back(std::move(fileName), info, lineNumber, column);
1501 errmsg.id = obj[
"addon"].get<std::string>() +
"-" + obj[
"errorId"].get<std::string>();
1502 if (misraC2023 &&
startsWith(errmsg.id,
"misra-c2012-"))
1503 errmsg.id =
"misra-c2023-" + errmsg.id.substr(12);
1505 const std::string severity = obj[
"severity"].get<std::string>();
1508 if (!
endsWith(errmsg.id,
"-logChecker"))
1518 errmsg.file0 = file0;
1530 std::vector<std::string> ctuInfoFiles;
1531 for (
const auto &f: files) {
1544 for (
const std::string &f: ctuInfoFiles)
1545 std::remove(f.c_str());
1564 std::list<ErrorMessage::FileLocation> loclist;
1565 if (!file.empty()) {
1566 loclist.emplace_back(file, 0, 0);
1569 std::ostringstream msg;
1572 msg <<
" of " << numberOfConfigurations <<
" configurations. Use --force to check all configurations.\n";
1574 msg <<
" configurations. Use --force to check all configurations. For more details, use --enable=information.\n";
1575 msg <<
"The checking of the file will be interrupted because there are too many "
1576 "#ifdef configurations. Checking of all #ifdef configurations can be forced "
1577 "by --force command line option or from GUI preferences. However that may "
1578 "increase the checking time.";
1580 msg <<
" For more details, use --enable=information.";
1587 "toomanyconfigs",
CWE398,
1600 std::list<ErrorMessage::FileLocation> loclist;
1601 if (!file.empty()) {
1602 loclist.emplace_back(file, 0, 0);
1608 "The configuration '" + configuration +
"' was not checked because its code equals another one.",
1609 "purgedConfiguration",
1628 std::set<std::string> macroNames;
1630 const std::string &file = msg.
callStack.back().getfile(
false);
1631 int lineNumber = msg.
callStack.back().line;
1634 macroNames = it->second;
1667 if (!
mErrorList.emplace(std::move(errmsg)).second)
1712 (*it)->getErrorMessages(&errorlogger, &s);
1720 std::string allIncludes;
1721 for (
const std::string &inc : fileSettings.
includePaths) {
1722 allIncludes = allIncludes +
"-I\"" + inc +
"\" ";
1728 constexpr
char exe[] =
"clang-tidy.exe";
1730 constexpr
char exe[] =
"clang-tidy";
1733 const std::string args =
"-quiet -checks=*,-clang-analyzer-*,-llvm* \"" + fileSettings.
filename +
"\" -- " + allIncludes + allDefines;
1736 std::cerr <<
"Failed to execute '" << exe <<
"' (exitcode: " << std::to_string(exitcode) <<
")" << std::endl;
1741 std::istringstream istr(output);
1746 std::ofstream fcmd(analyzerInfoFile +
".clang-tidy-cmd");
1750 while (std::getline(istr, line)) {
1751 if (line.find(
"error") == std::string::npos && line.find(
"warning") == std::string::npos)
1754 std::size_t endColumnPos = line.find(
": error:");
1755 if (endColumnPos == std::string::npos) {
1756 endColumnPos = line.find(
": warning:");
1759 const std::size_t endLinePos = line.rfind(
':', endColumnPos-1);
1760 const std::size_t endNamePos = line.rfind(
':', endLinePos - 1);
1761 const std::size_t endMsgTypePos = line.find(
':', endColumnPos + 2);
1762 const std::size_t endErrorPos = line.rfind(
'[', std::string::npos);
1763 if (endLinePos==std::string::npos || endNamePos==std::string::npos || endMsgTypePos==std::string::npos || endErrorPos==std::string::npos)
1766 const std::string lineNumString = line.substr(endNamePos + 1, endLinePos - endNamePos - 1);
1767 const std::string columnNumString = line.substr(endLinePos + 1, endColumnPos - endLinePos - 1);
1768 const std::string messageString = line.substr(endMsgTypePos + 1, endErrorPos - endMsgTypePos - 1);
1769 const std::string errorString = line.substr(endErrorPos, line.length());
1772 const int64_t lineNumber = strToInt<int64_t>(lineNumString);
1773 const int64_t column = strToInt<int64_t>(columnNumString);
1777 errmsg.
callStack.emplace_back(fixedpath, lineNumber, column);
1779 errmsg.
id =
"clang-tidy-" + errorString.substr(1, errorString.length() - 2);
1780 if (errmsg.
id.find(
"performance") != std::string::npos)
1782 else if (errmsg.
id.find(
"portability") != std::string::npos)
1784 else if (errmsg.
id.find(
"cert") != std::string::npos || errmsg.
id.find(
"misc") != std::string::npos || errmsg.
id.find(
"unused") != std::string::npos)
1789 errmsg.
file0 = std::move(fixedpath);
1790 errmsg.
setmsg(messageString);
1797 bool errors =
false;
1820 void CppCheck::analyseWholeProgram(
const std::string &buildDir,
const std::list<std::pair<std::string, std::size_t>> &files,
const std::list<FileSettings>& fileSettings)
1823 if (buildDir.empty()) {
1829 std::list<Check::FileInfo*> fileInfoList;
1833 const std::string filesTxt(buildDir +
"/files.txt");
1834 std::ifstream fin(filesTxt);
1835 std::string filesTxtLine;
1836 while (std::getline(fin, filesTxtLine)) {
1837 const std::string::size_type firstColon = filesTxtLine.find(
':');
1838 if (firstColon == std::string::npos)
1840 const std::string::size_type lastColon = filesTxtLine.rfind(
':');
1841 if (firstColon == lastColon)
1843 const std::string xmlfile = buildDir +
'/' + filesTxtLine.substr(0,firstColon);
1846 tinyxml2::XMLDocument doc;
1847 const tinyxml2::XMLError
error = doc.LoadFile(xmlfile.c_str());
1848 if (
error != tinyxml2::XML_SUCCESS)
1851 const tinyxml2::XMLElement *
const rootNode = doc.FirstChildElement();
1852 if (rootNode ==
nullptr)
1855 for (
const tinyxml2::XMLElement *e = rootNode->FirstChildElement(); e; e = e->NextSiblingElement()) {
1856 if (std::strcmp(e->Name(),
"FileInfo") != 0)
1858 const char *checkClassAttr = e->Attribute(
"check");
1859 if (!checkClassAttr)
1861 if (std::strcmp(checkClassAttr,
"ctu") == 0) {
1867 if (checkClassAttr ==
check->name())
1868 fileInfoList.push_back(
check->loadFileInfoFromXml(e));
1879 check->analyseWholeProgram(&ctuFileInfo, fileInfoList,
mSettings, *
this);
1891 for (
const auto& f: files) {
1894 std::remove(ctuInfoFileName.c_str());
1896 for (
const auto& fs: fileSettings) {
1899 std::remove(ctuInfoFileName.c_str());
std::list< NestedCall > nestedCalls
void loadFromXml(const tinyxml2::XMLElement *xmlElement)
std::list< FunctionCall > functionCalls
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.
Interface class that cppcheck uses to communicate with the checks.
static std::list< Check * > & instances()
List of registered check classes.
This is the base class which will use other classes to do static code analysis for C and C++ code to ...
bool analyseWholeProgram()
Analyse whole program, run this after all TUs has been scanned.
ErrorLogger & mErrorLogger
std::unordered_set< std::string > mErrorList
static void resetTimerResults()
unsigned int checkClang(const std::string &path)
void checkRawTokens(const Tokenizer &tokenizer)
Check raw tokens.
std::unique_ptr< CheckUnusedFunctions > mUnusedFunctionsCheck
static void printTimerResults(SHOWTIME_MODES mode)
void checkNormalTokens(const Tokenizer &tokenizer)
Check normal tokens.
Settings & settings()
Get reference to current settings.
AnalyzerInformation mAnalyzerInformation
void executeAddonsWholeProgram(const std::list< std::pair< std::string, std::size_t >> &files)
Execute addons.
unsigned int checkFile(const std::string &filename, const std::string &cfgname, std::istream *fileStream=nullptr)
Check a file using stream.
std::list< Check::FileInfo * > mFileInfo
File info used for whole program analysis.
void removeCtuInfoFiles(const std::list< std::pair< std::string, std::size_t >> &files, const std::list< FileSettings > &fileSettings)
Remove *.ctu-info files.
std::function< int(std::string, std::vector< std::string >, std::string, std::string &)> ExecuteCmdFn
static void getErrorMessages(ErrorLogger &errorlogger)
Call all "getErrorMessages" in all registered Check classes.
std::pair< std::string, int > Location
bool mUseGlobalSuppressions
std::string mCurrentConfig
Current preprocessor configuration.
void internalError(const std::string &filename, const std::string &msg)
There has been an internal error => Report information message.
static const char * version()
Returns current version number as a string.
void reportOut(const std::string &outmsg, Color c=Color::Reset) override
Information about progress is directed here.
unsigned int check(const std::string &path)
This starts the actual checking.
CppCheck(ErrorLogger &errorLogger, bool useGlobalSuppressions, ExecuteCmdFn executeCommand)
Constructor.
void reportProgress(const std::string &filename, const char stage[], const std::size_t value) override
Report progress to client.
~CppCheck() override
Destructor.
std::map< Location, std::set< std::string > > mLocationMacros
void analyseClangTidy(const FileSettings &fileSettings)
Analyze all files using clang-tidy.
bool isPremiumCodingStandardId(const std::string &id) const
void executeAddons(const std::vector< std::string > &files, const std::string &file0)
Execute addons.
bool mTooManyConfigs
Are there too many configs?
ExecuteCmdFn mExecuteCommand
Callback for executing a shell command (exe, args, output)
void purgedConfigurationMessage(const std::string &file, const std::string &configuration)
void tooManyConfigsError(const std::string &file, const int numberOfConfigurations)
static const char * extraVersion()
Returns extra version info as a string.
void reportErr(const ErrorMessage &msg) override
Errors and warnings are directed here.
A preprocessor directive Each preprocessor directive (#include, #define, #undef, #if,...
This is an interface, which the class responsible of error logging should implement.
static bool isCriticalErrorId(const std::string &id)
static std::string plistData(const ErrorMessage &msg)
static const char * plistFooter()
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.
File name and line number.
Wrapper for error messages, provided by reportErr()
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)
std::list< FileLocation > callStack
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
bool markupFile(const std::string &path) const
static std::string simplifyPath(std::string originalPath)
Simplify path "foo/bar/.." => "foo".
static std::string fromNativeSeparators(std::string path)
Convert path to use internal path separators.
static std::string toNativeSeparators(std::string path)
Convert path to use native separators.
static Standards::Language identify(const std::string &path, bool *header=nullptr)
Identify the language based on the file extension.
static std::string getPathFromFilename(const std::string &filename)
Lookup the path part from a filename (e.g., '/tmp/a.h' -> '/tmp/', 'a.h' -> '')
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.
The cppcheck preprocessor.
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
const std::list< Directive > & getDirectives() const
list of all directives met while preprocessing file
void setDirectives(const simplecpp::TokenList &tokens)
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)
void preprocess(std::istream &istr, std::map< std::string, std::string > &result, const std::string &filename, const std::list< std::string > &includePaths=std::list< std::string >())
Extract the code for each configuration.
static void getErrorMessages(ErrorLogger *errorLogger, const Settings &settings)
static char macroChar
character that is inserted in expanded macros
void dump(std::ostream &out) const
dump all directives present in source file
void inlineSuppressions(const simplecpp::TokenList &tokens, SuppressionList &suppressions)
This is just a container for general settings so that we don't need to pass individual values to func...
std::set< std::string > userUndefs
undefines given by the user
bool quiet
Is –quiet given?
bool isUnusedFunctionCheckEnabled() const
Check if the user wants to check for unused functions and if it's possible at all.
bool preprocessOnly
Using -E for debugging purposes.
bool checkAllConfigurations
check all configurations (false if -D or –max-configs is used
std::string getMisraRuleText(const std::string &id, const std::string &text) const
std::vector< std::string > basePaths
Paths used as base for conversion to relative paths.
int maxCtuDepth
–max-ctu-depth
Suppressions supprs
suppressions
std::string plistOutput
plist output (–plist-output=<dir>)
std::string clangExecutable
Custom Clang executable.
Standards::Language enforcedLang
Name of the language that is enforced.
SimpleEnableGroup< Checks > checks
bool checkConfiguration
Is the 'configuration checking' wanted?
static bool terminated()
termination requested?
bool relativePaths
Use relative paths in output.
std::unordered_set< std::string > addons
addons, either filename of python/json file or json data
bool safety
Safety certified behavior Show checkers report when Cppcheck finishes Make cppcheck checking more str...
std::string buildDir
–cppcheck-build-dir.
std::string userDefines
defines given by the user
std::vector< AddonInfo > addonInfos
the loaded addons infos
std::string premiumArgs
Extra arguments for Cppcheck Premium addon.
int checksMaxTime
The maximum time in seconds for the checks of a single file.
bool useSingleJob() const
bool debugnormal
Is –debug-normal given?
bool force
Force checking the files with "too many" configurations (–force).
bool verbose
Is –verbose given?
std::list< std::string > includePaths
List of include paths, e.g.
SHOWTIME_MODES showtime
show timing information (–showtime=file|summary|top5)
int maxConfigs
Maximum number of configurations to check before bailing.
SimpleEnableGroup< Severity > severity
std::string addonPython
Path to the python interpreter to be used to run addons.
bool debugwarnings
Is –debug-warnings given?
Standards standards
Struct contains standards settings.
bool isEnabled(T flag) const
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
const std::string & getSourceFilePath() const
std::size_t calculateHash() const
Calculates a hash of the token list used to compare multiple token lists with each other as quickly a...
void setLang(Standards::Language lang)
const std::vector< std::string > & getFiles() const
Get filenames (the sourcefile + the files it include).
bool createTokens(std::istream &code, const std::string &file0)
Create tokens from code.
int appendFileIfNew(std::string fileName)
append file name if seen the first time; return its index in any case
The token list that the TokenList generates is a linked-list of this class.
The main purpose is to tokenize the source code.
void printDebugOutput(int simplification) const
print –debug output if debug flags match the simplification: 0=unknown/both simplifications 1=1st sim...
const Token * tokens() const
bool simplifyTokens1(const std::string &configuration)
TokenList list
Token list: stores all tokens.
const SymbolDatabase * getSymbolDatabase() const
void dump(std::ostream &out) const
void setTimerResults(TimerResults *tr)
static const std::string emptyString
static constexpr char FILELIST[]
static std::string cmdFileName(std::string f)
static const CWE CWE398(398U)
static constexpr char ExtraVersion[]
static std::string detectPython(const CppCheck::ExecuteCmdFn &executeCommand)
static std::vector< std::string > split(const std::string &str, const std::string &sep=" ")
static std::string getDumpFileName(const Settings &settings, const std::string &filename)
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)
static constexpr char Version[]
static std::string getDefinesFlags(const std::string &semicolonSeparatedString)
static bool reportClangErrors(std::istream &is, const std::function< void(const ErrorMessage &)> &reportErr, std::vector< ErrorMessage > &warnings)
static TimerResults s_timerResults
static void createDumpFile(const Settings &settings, const std::string &filename, std::ofstream &fdump, std::string &dumpFile)
static simplecpp::TokenList createTokenList(const std::string &filename, std::vector< std::string > &files, simplecpp::OutputList *outputList, std::istream *fileStream)
static std::string getCtuInfoFileName(const std::string &dumpFile)
Severity severityFromString(const std::string &severity)
@ none
No severity (default value).
@ portability
Portability warning.
@ information
Checking information.
@ performance
Performance warning.
@ error
Programming error.
@ internal
Internal message.
CPPCHECKLIB FileInfo * getFileInfo(const Tokenizer *tokenizer)
Parse current TU and extract file info.
void setValues(TokenList &tokenlist, SymbolDatabase &symboldatabase, ErrorLogger *errorLogger, const Settings &settings, TimerResultsIntf *timerResults)
Perform valueflow analysis.
void CPPCHECKLIB parseClangAstDump(Tokenizer &tokenizer, std::istream &f)
static std::string cfg(const std::vector< std::string > &configs, const std::string &userDefines)
std::string cppcheckDefines() const
std::set< std::string > undefs
Platform::Type platformType
std::list< std::string > includePaths
std::list< std::string > systemIncludePaths
Simple container to be thrown when internal error is detected.
bool setC(const std::string &str)
std::string stdValue
–std value given on command line
bool setCPP(std::string str)
std::string getCPP() const
static SuppressionList::ErrorMessage fromErrorMessage(const ::ErrorMessage &msg, const std::set< std::string > ¯oNames)
SuppressionList nofail
suppress exitcode
SuppressionList nomsg
suppress message (–suppressions)
bool startsWith(const std::string &str, const char start[], std::size_t startlen)
bool endsWith(const std::string &str, char c)
#define CPPCHECK_VERSION_STRING