cppcheck/lib/cppcheck.cpp

1852 lines
73 KiB
C++
Raw Normal View History

2010-04-13 19:25:08 +02:00
/*
* Cppcheck - A tool for static C/C++ code analysis
2023-01-28 10:16:34 +01:00
* Copyright (C) 2007-2023 Cppcheck team.
2010-04-13 19:25:08 +02:00
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "cppcheck.h"
#include "check.h"
2016-10-29 12:18:11 +02:00
#include "checkunusedfunctions.h"
#include "clangimport.h"
2021-07-08 21:21:35 +02:00
#include "color.h"
2018-12-25 21:11:23 +01:00
#include "ctu.h"
#include "errortypes.h"
2017-05-27 04:33:47 +02:00
#include "library.h"
#include "mathlib.h"
#include "path.h"
#include "platform.h"
#include "preprocessor.h" // Preprocessor
#include "standards.h"
2017-05-27 04:33:47 +02:00
#include "suppressions.h"
#include "timer.h"
#include "token.h"
2017-05-27 04:33:47 +02:00
#include "tokenize.h" // Tokenizer
#include "tokenlist.h"
#include "utils.h"
#include "valueflow.h"
#include "version.h"
2016-10-29 12:18:11 +02:00
2010-04-13 19:25:08 +02:00
#include <algorithm>
#include <cstdio>
#include <cstdint>
2017-05-27 04:33:47 +02:00
#include <cstring>
#include <cctype>
#include <cstdlib>
#include <ctime>
#include <exception>
#include <fstream>
#include <iostream> // <- TEMPORARY
2017-05-27 04:33:47 +02:00
#include <new>
#include <set>
#include <sstream> // IWYU pragma: keep
#include <stdexcept>
#include <string>
#include <unordered_set>
#include <utility>
2017-05-27 04:33:47 +02:00
#include <vector>
dump: Fix concurrency problem with dump files (#4757) * dump: Fix concurrency problem with dump files This adds the process ID for the cppcheck process to the filenames of the .dump and .ctu-info files that the process generates. So lib/cppcheck.cpp.dump becomes lib/cppcheck.cpp.<PID>.dump For example: lib/cppcheck.cpp.2637871.dump The reason for this change is that if there is a buildsystem which supports concurrency, multiple instances of cppcheck may be run for the same file. For example, if the same file is compiled in multiple build variants, or for multiple targets. If running the MISRA plugin over such a project with concurrency enabled in the buildsystem, the plugin ends up crashing as multiple jobs attempt to create/trample/delete the same files while other jobs are using them. For more information see: https://sourceforge.net/p/cppcheck/discussion/general/thread/02c757b4af/ * dump: Include pid in filename if dump not explicit Only change the dump and ctu-info filenames to include the PID if they are being generated due to an addon. This means that existing scripts that use `--dump` will still work if they depend on the previous naming behaviour. The more robust filenames containing the pid will be used when the dump files are used as an internal implementation detail for passing data to addons. However this means that anything that does explicitly use `--dump` will be susceptible to concurrency problems. * test: Update addon dump file test to account for pid This test causes a dump file to be created by enabling the misra addon. Since the dump files now include the cppcheck process pid this test had to be updated to account for the change.
2023-02-13 20:54:21 +01:00
#ifndef _WIN32
#include <unistd.h>
#else
#include <process.h>
#endif
#define PICOJSON_USE_INT64
#include <picojson.h>
#include <simplecpp.h>
#include <tinyxml2.h>
2010-04-13 19:25:08 +02:00
#ifdef HAVE_RULES
#ifdef _WIN32
2010-12-12 11:56:22 +01:00
#define PCRE_STATIC
#endif
2010-12-12 11:56:22 +01:00
#include <pcre.h>
2010-12-31 10:24:51 +01:00
#endif
2010-12-12 11:56:22 +01:00
class SymbolDatabase;
static const char Version[] = CPPCHECK_VERSION_STRING;
static const char ExtraVersion[] = "";
static const char FILELIST[] = "cppcheck-addon-ctu-file-list";
static TimerResults s_timerResults;
2010-04-13 19:25:08 +02:00
// CWE ids used
static const CWE CWE398(398U); // Indicator of Poor Code Quality
namespace {
struct AddonInfo {
2019-04-14 15:00:03 +02:00
std::string name;
2021-10-11 19:26:51 +02:00
std::string scriptFile; // addon script
std::string executable; // addon executable
std::string args; // special extra arguments
std::string python; // script interpreter
2021-07-07 10:58:13 +02:00
bool ctu = false;
2022-08-22 21:11:28 +02:00
std::string runScript;
static std::string getFullPath(const std::string &fileName, const std::string &exename) {
2019-06-16 15:46:01 +02:00
if (Path::fileExists(fileName))
return fileName;
const std::string exepath = Path::getPathFromFilename(exename);
if (Path::fileExists(exepath + fileName))
return exepath + fileName;
if (Path::fileExists(exepath + "addons/" + fileName))
return exepath + "addons/" + fileName;
#ifdef FILESDIR
if (Path::fileExists(FILESDIR + ("/" + fileName)))
return FILESDIR + ("/" + fileName);
if (Path::fileExists(FILESDIR + ("/addons/" + fileName)))
return FILESDIR + ("/addons/" + fileName);
2019-06-16 15:46:01 +02:00
#endif
return "";
}
std::string parseAddonInfo(const picojson::value &json, const std::string &fileName, const std::string &exename) {
const std::string& json_error = picojson::get_last_error();
if (!json_error.empty()) {
return "Loading " + fileName + " failed. " + json_error;
}
if (!json.is<picojson::object>())
return "Loading " + fileName + " failed. Bad json.";
picojson::object obj = json.get<picojson::object>();
if (obj.count("args")) {
if (!obj["args"].is<picojson::array>())
return "Loading " + fileName + " failed. args must be array.";
for (const picojson::value &v : obj["args"].get<picojson::array>())
args += " " + v.get<std::string>();
}
2021-07-07 10:58:13 +02:00
if (obj.count("ctu")) {
// ctu is specified in the config file
if (!obj["ctu"].is<bool>())
2021-10-11 19:26:51 +02:00
return "Loading " + fileName + " failed. ctu must be boolean.";
2021-07-07 10:58:13 +02:00
ctu = obj["ctu"].get<bool>();
} else {
ctu = false;
}
if (obj.count("python")) {
// Python was defined in the config file
if (obj["python"].is<picojson::array>()) {
return "Loading " + fileName +" failed. python must not be an array.";
}
python = obj["python"].get<std::string>();
} else {
python = "";
}
2021-10-11 19:26:51 +02:00
if (obj.count("executable")) {
if (!obj["executable"].is<std::string>())
return "Loading " + fileName + " failed. executable must be a string.";
executable = getFullPath(obj["executable"].get<std::string>(), fileName);
2021-10-11 19:26:51 +02:00
return "";
}
return getAddonInfo(obj["script"].get<std::string>(), exename);
}
2019-04-14 15:00:03 +02:00
std::string getAddonInfo(const std::string &fileName, const std::string &exename) {
if (fileName[0] == '{') {
std::istringstream in(fileName);
picojson::value json;
in >> json;
return parseAddonInfo(json, fileName, exename);
}
if (fileName.find('.') == std::string::npos)
2019-06-16 19:11:12 +02:00
return getAddonInfo(fileName + ".py", exename);
if (endsWith(fileName, ".py")) {
2019-06-16 15:46:01 +02:00
scriptFile = getFullPath(fileName, exename);
if (scriptFile.empty())
return "Did not find addon " + fileName;
std::string::size_type pos1 = scriptFile.rfind('/');
2019-06-16 15:46:01 +02:00
if (pos1 == std::string::npos)
pos1 = 0;
else
pos1++;
std::string::size_type pos2 = scriptFile.rfind('.');
2019-06-16 15:46:01 +02:00
if (pos2 < pos1)
pos2 = std::string::npos;
name = scriptFile.substr(pos1, pos2 - pos1);
runScript = getFullPath("runaddon.py", exename);
return "";
}
2019-06-16 15:46:01 +02:00
if (!endsWith(fileName, ".json"))
2019-06-16 15:46:01 +02:00
return "Failed to open addon " + fileName;
std::ifstream fin(fileName);
if (!fin.is_open())
return "Failed to open " + fileName;
picojson::value json;
fin >> json;
return parseAddonInfo(json, fileName, exename);
}
};
}
static std::string cmdFileName(std::string f)
{
2020-05-19 08:14:56 +02:00
f = Path::toNativeSeparators(f);
if (f.find(' ') != std::string::npos)
return "\"" + f + "\"";
return f;
}
static std::vector<std::string> split(const std::string &str, const std::string &sep=" ")
2020-05-19 08:14:56 +02:00
{
std::vector<std::string> ret;
for (std::string::size_type startPos = 0U; startPos < str.size();) {
startPos = str.find_first_not_of(sep, startPos);
if (startPos == std::string::npos)
break;
if (str[startPos] == '\"') {
const std::string::size_type endPos = str.find('\"', startPos + 1);
ret.push_back(str.substr(startPos + 1, endPos - startPos - 1));
startPos = (endPos < str.size()) ? (endPos + 1) : endPos;
continue;
}
const std::string::size_type endPos = str.find(sep, startPos + 1);
ret.push_back(str.substr(startPos, endPos - startPos));
startPos = endPos;
}
return ret;
2020-05-19 08:14:56 +02:00
}
dump: Fix concurrency problem with dump files (#4757) * dump: Fix concurrency problem with dump files This adds the process ID for the cppcheck process to the filenames of the .dump and .ctu-info files that the process generates. So lib/cppcheck.cpp.dump becomes lib/cppcheck.cpp.<PID>.dump For example: lib/cppcheck.cpp.2637871.dump The reason for this change is that if there is a buildsystem which supports concurrency, multiple instances of cppcheck may be run for the same file. For example, if the same file is compiled in multiple build variants, or for multiple targets. If running the MISRA plugin over such a project with concurrency enabled in the buildsystem, the plugin ends up crashing as multiple jobs attempt to create/trample/delete the same files while other jobs are using them. For more information see: https://sourceforge.net/p/cppcheck/discussion/general/thread/02c757b4af/ * dump: Include pid in filename if dump not explicit Only change the dump and ctu-info filenames to include the PID if they are being generated due to an addon. This means that existing scripts that use `--dump` will still work if they depend on the previous naming behaviour. The more robust filenames containing the pid will be used when the dump files are used as an internal implementation detail for passing data to addons. However this means that anything that does explicitly use `--dump` will be susceptible to concurrency problems. * test: Update addon dump file test to account for pid This test causes a dump file to be created by enabling the misra addon. Since the dump files now include the cppcheck process pid this test had to be updated to account for the change.
2023-02-13 20:54:21 +01:00
static int getPid()
{
#ifndef _WIN32
return getpid();
#else
return _getpid();
#endif
}
2021-07-07 10:58:13 +02:00
static std::string getDumpFileName(const Settings& settings, const std::string& filename)
{
if (!settings.dumpFile.empty())
return settings.dumpFile;
dump: Fix concurrency problem with dump files (#4757) * dump: Fix concurrency problem with dump files This adds the process ID for the cppcheck process to the filenames of the .dump and .ctu-info files that the process generates. So lib/cppcheck.cpp.dump becomes lib/cppcheck.cpp.<PID>.dump For example: lib/cppcheck.cpp.2637871.dump The reason for this change is that if there is a buildsystem which supports concurrency, multiple instances of cppcheck may be run for the same file. For example, if the same file is compiled in multiple build variants, or for multiple targets. If running the MISRA plugin over such a project with concurrency enabled in the buildsystem, the plugin ends up crashing as multiple jobs attempt to create/trample/delete the same files while other jobs are using them. For more information see: https://sourceforge.net/p/cppcheck/discussion/general/thread/02c757b4af/ * dump: Include pid in filename if dump not explicit Only change the dump and ctu-info filenames to include the PID if they are being generated due to an addon. This means that existing scripts that use `--dump` will still work if they depend on the previous naming behaviour. The more robust filenames containing the pid will be used when the dump files are used as an internal implementation detail for passing data to addons. However this means that anything that does explicitly use `--dump` will be susceptible to concurrency problems. * test: Update addon dump file test to account for pid This test causes a dump file to be created by enabling the misra addon. Since the dump files now include the cppcheck process pid this test had to be updated to account for the change.
2023-02-13 20:54:21 +01:00
std::string extension;
if (settings.dump)
extension = ".dump";
else
extension = "." + std::to_string(getPid()) + ".dump";
2021-07-07 10:58:13 +02:00
if (!settings.dump && !settings.buildDir.empty())
dump: Fix concurrency problem with dump files (#4757) * dump: Fix concurrency problem with dump files This adds the process ID for the cppcheck process to the filenames of the .dump and .ctu-info files that the process generates. So lib/cppcheck.cpp.dump becomes lib/cppcheck.cpp.<PID>.dump For example: lib/cppcheck.cpp.2637871.dump The reason for this change is that if there is a buildsystem which supports concurrency, multiple instances of cppcheck may be run for the same file. For example, if the same file is compiled in multiple build variants, or for multiple targets. If running the MISRA plugin over such a project with concurrency enabled in the buildsystem, the plugin ends up crashing as multiple jobs attempt to create/trample/delete the same files while other jobs are using them. For more information see: https://sourceforge.net/p/cppcheck/discussion/general/thread/02c757b4af/ * dump: Include pid in filename if dump not explicit Only change the dump and ctu-info filenames to include the PID if they are being generated due to an addon. This means that existing scripts that use `--dump` will still work if they depend on the previous naming behaviour. The more robust filenames containing the pid will be used when the dump files are used as an internal implementation detail for passing data to addons. However this means that anything that does explicitly use `--dump` will be susceptible to concurrency problems. * test: Update addon dump file test to account for pid This test causes a dump file to be created by enabling the misra addon. Since the dump files now include the cppcheck process pid this test had to be updated to account for the change.
2023-02-13 20:54:21 +01:00
return AnalyzerInformation::getAnalyzerInfoFile(settings.buildDir, filename, emptyString) + extension;
return filename + extension;
2021-07-07 10:58:13 +02:00
}
static std::string getCtuInfoFileName(const std::string &dumpFile)
{
return dumpFile.substr(0, dumpFile.size()-4) + "ctu-info";
}
static void createDumpFile(const Settings& settings,
const std::string& filename,
std::ofstream& fdump,
std::string& dumpFile)
{
if (!settings.dump && settings.addons.empty())
return;
2021-07-07 10:58:13 +02:00
dumpFile = getDumpFileName(settings, filename);
fdump.open(dumpFile);
if (!fdump.is_open())
return;
2021-07-07 10:58:13 +02:00
{
std::ofstream fout(getCtuInfoFileName(dumpFile));
}
2022-11-20 12:29:56 +01:00
std::string language;
switch (settings.enforcedLang) {
case Settings::Language::C:
language = " language=\"c\"";
break;
case Settings::Language::CPP:
language = " language=\"cpp\"";
break;
case Settings::Language::None:
if (Path::isCPP(filename))
language = " language=\"cpp\"";
else if (Path::isC(filename))
language = " language=\"c\"";
break;
}
fdump << "<?xml version=\"1.0\"?>" << std::endl;
2022-11-20 12:29:56 +01:00
fdump << "<dumps" << language << ">" << std::endl;
fdump << " <platform"
<< " name=\"" << settings.platform.toString() << '\"'
<< " char_bit=\"" << settings.platform.char_bit << '\"'
<< " short_bit=\"" << settings.platform.short_bit << '\"'
<< " int_bit=\"" << settings.platform.int_bit << '\"'
<< " long_bit=\"" << settings.platform.long_bit << '\"'
<< " long_long_bit=\"" << settings.platform.long_long_bit << '\"'
<< " pointer_bit=\"" << (settings.platform.sizeof_pointer * settings.platform.char_bit) << '\"'
<< "/>\n";
}
static std::string executeAddon(const AddonInfo &addonInfo,
const std::string &defaultPythonExe,
const std::string &file,
2022-08-22 21:11:28 +02:00
const std::string &premiumArgs,
const std::function<bool(std::string,std::vector<std::string>,std::string,std::string&)> &executeCommand)
2020-04-05 10:57:51 +02:00
{
const std::string redirect = "2>&1";
std::string pythonExe;
2021-10-11 19:26:51 +02:00
if (!addonInfo.executable.empty())
pythonExe = addonInfo.executable;
else if (!addonInfo.python.empty())
2020-05-19 08:14:56 +02:00
pythonExe = cmdFileName(addonInfo.python);
else if (!defaultPythonExe.empty())
pythonExe = cmdFileName(defaultPythonExe);
else {
#ifdef _WIN32
const char *py_exes[] = { "python3.exe", "python.exe" };
#else
const char *py_exes[] = { "python3", "python" };
#endif
for (const char* py_exe : py_exes) {
2020-05-19 08:14:56 +02:00
std::string out;
if (executeCommand(py_exe, split("--version"), redirect, out) && out.compare(0, 7, "Python ") == 0 && std::isdigit(out[7])) {
pythonExe = py_exe;
2020-05-19 08:14:56 +02:00
break;
}
}
2020-05-19 08:14:56 +02:00
if (pythonExe.empty())
throw InternalError(nullptr, "Failed to auto detect python");
}
2021-10-11 19:26:51 +02:00
std::string args;
if (addonInfo.executable.empty())
args = cmdFileName(addonInfo.runScript) + " " + cmdFileName(addonInfo.scriptFile);
args += std::string(args.empty() ? "" : " ") + "--cli" + addonInfo.args;
2022-08-22 21:11:28 +02:00
if (!premiumArgs.empty() && !addonInfo.executable.empty())
args += " " + premiumArgs;
2021-10-11 19:26:51 +02:00
const std::string fileArg = (endsWith(file, FILELIST, sizeof(FILELIST)-1) ? " --file-list " : " ") + cmdFileName(file);
2021-10-11 19:26:51 +02:00
args += fileArg;
2019-04-14 15:00:03 +02:00
std::string result;
if (!executeCommand(pythonExe, split(args), redirect, result)) {
std::string message("Failed to execute addon (command: '" + pythonExe + " " + args + "'). Exitcode is nonzero.");
if (result.size() > 2) {
message = message + "\n" + message + "\nOutput:\n" + result;
message.resize(message.find_last_not_of("\n\r"));
}
throw InternalError(nullptr, message);
}
// Validate output..
std::istringstream istr(result);
std::string line;
while (std::getline(istr, line)) {
if (line.compare(0,9,"Checking ", 0, 9) != 0 && !line.empty() && line[0] != '{') {
result.erase(result.find_last_not_of('\n') + 1, std::string::npos); // Remove trailing newlines
2020-05-19 08:14:56 +02:00
throw InternalError(nullptr, "Failed to execute '" + pythonExe + " " + args + "'. " + result);
}
}
// Valid results
2019-04-14 15:00:03 +02:00
return result;
}
static std::string getDefinesFlags(const std::string &semicolonSeparatedString)
{
std::string flags;
for (const std::string &d: split(semicolonSeparatedString, ";"))
flags += "-D" + d + " ";
return flags;
}
CppCheck::CppCheck(ErrorLogger &errorLogger,
bool useGlobalSuppressions,
std::function<bool(std::string,std::vector<std::string>,std::string,std::string&)> executeCommand)
: mErrorLogger(errorLogger)
, mExitCode(0)
, mUseGlobalSuppressions(useGlobalSuppressions)
, mTooManyConfigs(false)
, mSimplify(true)
, mExecuteCommand(std::move(executeCommand))
2021-08-07 20:51:18 +02:00
{}
2010-04-13 19:25:08 +02:00
CppCheck::~CppCheck()
{
2018-06-17 07:36:05 +02:00
while (!mFileInfo.empty()) {
2018-06-17 07:40:13 +02:00
delete mFileInfo.back();
mFileInfo.pop_back();
}
s_timerResults.showResults(mSettings.showtime);
if (mPlistFile.is_open()) {
mPlistFile << ErrorLogger::plistFooter();
mPlistFile.close();
}
2010-04-13 19:25:08 +02:00
}
const char * CppCheck::version()
2010-04-13 19:25:08 +02:00
{
return Version;
}
const char * CppCheck::extraVersion()
{
return ExtraVersion;
2010-04-13 19:25:08 +02:00
}
static bool reportClangErrors(std::istream &is, const std::function<void(const ErrorMessage&)>& reportErr, std::vector<ErrorMessage> &warnings)
{
std::string line;
while (std::getline(is, line)) {
if (line.empty() || line[0] == ' ' || line[0] == '`' || line[0] == '-')
continue;
std::string::size_type pos3 = line.find(": error: ");
if (pos3 == std::string::npos)
pos3 = line.find(": fatal error:");
if (pos3 == std::string::npos)
2021-12-11 12:42:15 +01:00
pos3 = line.find(": warning:");
if (pos3 == std::string::npos)
continue;
// file:line:column: error: ....
const std::string::size_type pos2 = line.rfind(':', pos3 - 1);
const std::string::size_type pos1 = line.rfind(':', pos2 - 1);
if (pos1 >= pos2 || pos2 >= pos3)
continue;
const std::string filename = line.substr(0, pos1);
const std::string linenr = line.substr(pos1+1, pos2-pos1-1);
const std::string colnr = line.substr(pos2+1, pos3-pos2-1);
const std::string msg = line.substr(line.find(':', pos3+1) + 2);
const std::string locFile = Path::toNativeSeparators(filename);
ErrorMessage::FileLocation loc;
loc.setfile(locFile);
loc.line = strToInt<int>(linenr);
loc.column = strToInt<unsigned int>(colnr);
ErrorMessage errmsg({std::move(loc)},
locFile,
Severity::error,
msg,
"syntaxError",
Certainty::normal);
2021-12-11 12:42:15 +01:00
if (line.compare(pos3, 10, ": warning:") == 0) {
warnings.push_back(std::move(errmsg));
2021-12-11 12:42:15 +01:00
continue;
}
reportErr(errmsg);
return true;
}
return false;
}
unsigned int CppCheck::check(const std::string &path)
2010-04-13 19:25:08 +02:00
{
if (mSettings.clang) {
if (!mSettings.quiet)
2021-07-08 21:21:35 +02:00
mErrorLogger.reportOut(std::string("Checking ") + path + "...", Color::FgGreen);
const std::string lang = Path::isCPP(path) ? "-x c++" : "-x c";
const std::string analyzerInfo = mSettings.buildDir.empty() ? std::string() : AnalyzerInformation::getAnalyzerInfoFile(mSettings.buildDir, path, emptyString);
const std::string clangcmd = analyzerInfo + ".clang-cmd";
const std::string clangStderr = analyzerInfo + ".clang-stderr";
const std::string clangAst = analyzerInfo + ".clang-ast";
std::string exe = mSettings.clangExecutable;
#ifdef _WIN32
// append .exe if it is not a path
if (Path::fromNativeSeparators(mSettings.clangExecutable).find('/') == std::string::npos) {
exe += ".exe";
}
#endif
std::string flags(lang + " ");
if (Path::isCPP(path) && !mSettings.standards.stdValue.empty())
flags += "-std=" + mSettings.standards.stdValue + " ";
for (const std::string &i: mSettings.includePaths)
flags += "-I" + i + " ";
flags += getDefinesFlags(mSettings.userDefines);
const std::string args2 = "-fsyntax-only -Xclang -ast-dump -fno-color-diagnostics " + flags + path;
const std::string redirect2 = analyzerInfo.empty() ? std::string("2>&1") : ("2> " + clangStderr);
if (!mSettings.buildDir.empty()) {
std::ofstream fout(clangcmd);
fout << exe << " " << args2 << " " << redirect2 << std::endl;
} else if (mSettings.verbose && !mSettings.quiet) {
mErrorLogger.reportOut(exe + " " + args2);
}
std::string output2;
if (!mExecuteCommand(exe,split(args2),redirect2,output2) || output2.find("TranslationUnitDecl") == std::string::npos) {
std::cerr << "Failed to execute '" << exe << " " << args2 << " " << redirect2 << "'" << std::endl;
return 0;
}
// Ensure there are not syntax errors...
2021-12-11 12:42:15 +01:00
std::vector<ErrorMessage> compilerWarnings;
if (!mSettings.buildDir.empty()) {
std::ifstream fin(clangStderr);
auto reportError = [this](const ErrorMessage& errorMessage) {
reportErr(errorMessage);
};
if (reportClangErrors(fin, reportError, compilerWarnings))
return 0;
} else {
std::istringstream istr(output2);
auto reportError = [this](const ErrorMessage& errorMessage) {
reportErr(errorMessage);
};
if (reportClangErrors(istr, reportError, compilerWarnings))
return 0;
}
if (!mSettings.buildDir.empty()) {
std::ofstream fout(clangAst);
fout << output2 << std::endl;
}
try {
std::istringstream ast(output2);
Tokenizer tokenizer(&mSettings, this);
tokenizer.list.appendFileIfNew(path);
clangimport::parseClangAstDump(&tokenizer, ast);
ValueFlow::setValues(&tokenizer.list, const_cast<SymbolDatabase *>(tokenizer.getSymbolDatabase()), this, &mSettings);
if (mSettings.debugnormal)
tokenizer.printDebugOutput(1);
checkNormalTokens(tokenizer);
// create dumpfile
std::ofstream fdump;
std::string dumpFile;
createDumpFile(mSettings, path, fdump, dumpFile);
if (fdump.is_open()) {
fdump << "<dump cfg=\"\">" << std::endl;
2021-12-11 12:42:15 +01:00
for (const ErrorMessage& errmsg: compilerWarnings)
fdump << " <clang-warning file=\"" << toxml(errmsg.callStack.front().getfile()) << "\" line=\"" << errmsg.callStack.front().line << "\" column=\"" << errmsg.callStack.front().column << "\" message=\"" << toxml(errmsg.shortMessage()) << "\"/>\n";
fdump << " <standards>" << std::endl;
fdump << " <c version=\"" << mSettings.standards.getC() << "\"/>" << std::endl;
fdump << " <cpp version=\"" << mSettings.standards.getCPP() << "\"/>" << std::endl;
fdump << " </standards>" << std::endl;
tokenizer.dump(fdump);
fdump << "</dump>" << std::endl;
fdump << "</dumps>" << std::endl;
fdump.close();
}
// run addons
executeAddons(dumpFile);
} catch (const InternalError &e) {
internalError(path, e.errorMessage);
mExitCode = 1; // e.g. reflect a syntax error
} catch (const TerminateException &) {
// Analysis is terminated
return mExitCode;
} catch (const std::exception &e) {
internalError(path, e.what());
}
return mExitCode;
}
std::ifstream fin(path);
return checkFile(Path::simplifyPath(path), emptyString, fin);
2010-04-13 19:25:08 +02:00
}
unsigned int CppCheck::check(const std::string &path, const std::string &content)
2010-04-13 19:25:08 +02:00
{
std::istringstream iss(content);
return checkFile(Path::simplifyPath(path), emptyString, iss);
2010-04-13 19:25:08 +02:00
}
unsigned int CppCheck::check(const ImportProject::FileSettings &fs)
{
CppCheck temp(mErrorLogger, mUseGlobalSuppressions, mExecuteCommand);
temp.mSettings = mSettings;
if (!temp.mSettings.userDefines.empty())
temp.mSettings.userDefines += ';';
if (mSettings.clang)
temp.mSettings.userDefines += fs.defines;
else
temp.mSettings.userDefines += fs.cppcheckDefines();
temp.mSettings.includePaths = fs.includePaths;
temp.mSettings.userUndefs.insert(fs.undefs.cbegin(), fs.undefs.cend());
if (fs.standard.find("++") != std::string::npos)
temp.mSettings.standards.setCPP(fs.standard);
else if (!fs.standard.empty())
temp.mSettings.standards.setC(fs.standard);
if (fs.platformType != cppcheck::Platform::Type::Unspecified)
temp.mSettings.platform.set(fs.platformType);
if (mSettings.clang) {
temp.mSettings.includePaths.insert(temp.mSettings.includePaths.end(), fs.systemIncludePaths.cbegin(), fs.systemIncludePaths.cend());
return temp.check(Path::simplifyPath(fs.filename));
}
std::ifstream fin(fs.filename);
const unsigned int returnValue = temp.checkFile(Path::simplifyPath(fs.filename), fs.cfg, fin);
mSettings.nomsg.addSuppressions(temp.mSettings.nomsg.getSuppressions());
return returnValue;
}
unsigned int CppCheck::checkFile(const std::string& filename, const std::string &cfgname, std::istream& fileStream)
2010-04-13 19:25:08 +02:00
{
2018-06-17 07:43:25 +02:00
mExitCode = 0;
2010-04-13 19:25:08 +02:00
2012-10-03 19:51:09 +02:00
// only show debug warnings for accepted C/C++ source files
if (!Path::acceptFile(filename))
mSettings.debugwarnings = false;
if (Settings::terminated())
2018-06-17 07:43:25 +02:00
return mExitCode;
if (!mSettings.quiet) {
std::string fixedpath = Path::simplifyPath(filename);
fixedpath = Path::toNativeSeparators(fixedpath);
2021-07-08 21:21:35 +02:00
mErrorLogger.reportOut(std::string("Checking ") + fixedpath + ' ' + cfgname + std::string("..."), Color::FgGreen);
if (mSettings.verbose) {
mErrorLogger.reportOut("Defines:" + mSettings.userDefines);
std::string undefs;
for (const std::string& U : mSettings.userUndefs) {
if (!undefs.empty())
undefs += ';';
undefs += ' ' + U;
}
mErrorLogger.reportOut("Undefines:" + undefs);
std::string includePaths;
for (const std::string &I : mSettings.includePaths)
includePaths += " -I" + I;
mErrorLogger.reportOut("Includes:" + includePaths);
mErrorLogger.reportOut(std::string("Platform:") + mSettings.platform.toString());
}
}
2010-04-13 19:25:08 +02:00
if (mPlistFile.is_open()) {
mPlistFile << ErrorLogger::plistFooter();
mPlistFile.close();
2017-05-16 14:07:23 +02:00
}
CheckUnusedFunctions checkUnusedFunctions(nullptr, nullptr, nullptr);
2011-10-13 20:53:06 +02:00
try {
Preprocessor preprocessor(mSettings, mSettings.nomsg, this);
2016-07-20 12:21:00 +02:00
std::set<std::string> configurations;
simplecpp::OutputList outputList;
std::vector<std::string> files;
simplecpp::TokenList tokens1(fileStream, files, filename, &outputList);
// If there is a syntax error, report it and stop
2018-11-05 09:49:28 +01:00
for (const simplecpp::Output &output : outputList) {
2017-09-12 22:42:10 +02:00
bool err;
2018-11-05 09:49:28 +01:00
switch (output.type) {
2017-09-12 22:42:10 +02:00
case simplecpp::Output::ERROR:
case simplecpp::Output::INCLUDE_NESTED_TOO_DEEPLY:
case simplecpp::Output::SYNTAX_ERROR:
case simplecpp::Output::UNHANDLED_CHAR_ERROR:
case simplecpp::Output::EXPLICIT_INCLUDE_NOT_FOUND:
2017-09-12 22:42:10 +02:00
err = true;
break;
case simplecpp::Output::WARNING:
case simplecpp::Output::MISSING_HEADER:
case simplecpp::Output::PORTABILITY_BACKSLASH:
err = false;
break;
}
2017-09-12 22:42:10 +02:00
if (err) {
std::string file = Path::fromNativeSeparators(output.location.file());
if (mSettings.relativePaths)
file = Path::getRelativePath(file, mSettings.basePaths);
const ErrorMessage::FileLocation loc1(file, output.location.line, output.location.col);
2020-05-23 07:16:49 +02:00
std::list<ErrorMessage::FileLocation> callstack(1, loc1);
ErrorMessage errmsg(callstack,
"",
Severity::error,
output.msg,
"syntaxError",
Certainty::normal);
reportErr(errmsg);
return mExitCode;
2017-09-12 22:42:10 +02:00
}
}
if (!preprocessor.loadFiles(tokens1, files))
return mExitCode;
if (!mSettings.plistOutput.empty()) {
2017-05-16 14:07:23 +02:00
std::string filename2;
if (filename.find('/') != std::string::npos)
filename2 = filename.substr(filename.rfind('/') + 1);
2017-05-16 14:07:23 +02:00
else
filename2 = filename;
const std::size_t fileNameHash = std::hash<std::string> {}(filename);
2020-11-13 15:52:24 +01:00
filename2 = mSettings.plistOutput + filename2.substr(0, filename2.find('.')) + "_" + std::to_string(fileNameHash) + ".plist";
mPlistFile.open(filename2);
mPlistFile << ErrorLogger::plistHeader(version(), files);
2017-05-16 14:07:23 +02:00
}
std::ostringstream dumpProlog;
if (mSettings.dump || !mSettings.addons.empty()) {
dumpProlog << " <rawtokens>" << std::endl;
for (unsigned int i = 0; i < files.size(); ++i)
dumpProlog << " <file index=\"" << i << "\" name=\"" << ErrorLogger::toxml(files[i]) << "\"/>" << std::endl;
for (const simplecpp::Token *tok = tokens1.cfront(); tok; tok = tok->next) {
dumpProlog
<< " <tok "
<< "fileIndex=\"" << tok->location.fileIndex << "\" "
<< "linenr=\"" << tok->location.line << "\" "
<< "column=\"" << tok->location.col << "\" "
<< "str=\"" << ErrorLogger::toxml(tok->str()) << "\""
<< "/>" << std::endl;
}
dumpProlog << " </rawtokens>" << std::endl;
}
2017-04-14 10:46:35 +02:00
// Parse comments and then remove them
preprocessor.inlineSuppressions(tokens1);
if (mSettings.dump || !mSettings.addons.empty()) {
mSettings.nomsg.dump(dumpProlog);
}
tokens1.removeComments();
preprocessor.removeComments();
2016-07-20 12:21:00 +02:00
if (!mSettings.buildDir.empty()) {
// Get toolinfo
std::ostringstream toolinfo;
toolinfo << CPPCHECK_VERSION_STRING;
toolinfo << (mSettings.severity.isEnabled(Severity::warning) ? 'w' : ' ');
toolinfo << (mSettings.severity.isEnabled(Severity::style) ? 's' : ' ');
toolinfo << (mSettings.severity.isEnabled(Severity::performance) ? 'p' : ' ');
toolinfo << (mSettings.severity.isEnabled(Severity::portability) ? 'p' : ' ');
toolinfo << (mSettings.severity.isEnabled(Severity::information) ? 'i' : ' ');
toolinfo << mSettings.userDefines;
mSettings.nomsg.dump(toolinfo);
// Calculate hash so it can be compared with old hash / future hashes
const std::size_t hash = preprocessor.calculateHash(tokens1, toolinfo.str());
2020-05-23 07:16:49 +02:00
std::list<ErrorMessage> errors;
if (!mAnalyzerInformation.analyzeFile(mSettings.buildDir, filename, cfgname, hash, errors)) {
2016-10-29 12:18:11 +02:00
while (!errors.empty()) {
reportErr(errors.front());
errors.pop_front();
}
2018-06-17 07:43:25 +02:00
return mExitCode; // known results => no need to reanalyze file
2016-10-29 12:18:11 +02:00
}
}
// write dump file xml prolog
std::ofstream fdump;
std::string dumpFile;
createDumpFile(mSettings, filename, fdump, dumpFile);
if (fdump.is_open()) {
fdump << dumpProlog.str();
dumpProlog.str("");
}
// Get directives
preprocessor.setDirectives(tokens1);
2017-05-17 21:58:46 +02:00
preprocessor.simplifyPragmaAsm(&tokens1);
2016-07-25 14:52:23 +02:00
preprocessor.setPlatformInfo(&tokens1);
2016-07-20 12:21:00 +02:00
// Get configurations..
if ((mSettings.checkAllConfigurations && mSettings.userDefines.empty()) || mSettings.force) {
Timer t("Preprocessor::getConfigs", mSettings.showtime, &s_timerResults);
2016-07-20 12:21:00 +02:00
configurations = preprocessor.getConfigs(tokens1);
} else {
configurations.insert(mSettings.userDefines);
}
2010-04-13 19:25:08 +02:00
if (mSettings.checkConfiguration) {
for (const std::string &config : configurations)
(void)preprocessor.getcode(tokens1, config, files, true);
return 0;
}
// Run define rules on raw code
const auto it = std::find_if(mSettings.rules.cbegin(), mSettings.rules.cend(), [](const Settings::Rule& rule) {
return rule.tokenlist == "define";
});
if (it != mSettings.rules.cend()) {
2016-07-28 15:35:41 +02:00
std::string code;
const std::list<Directive> &directives = preprocessor.getDirectives();
for (const Directive &dir : directives) {
if (dir.str.compare(0,8,"#define ") == 0 || dir.str.compare(0,9,"#include ") == 0)
code += "#line " + MathLib::toString(dir.linenr) + " \"" + dir.file + "\"\n" + dir.str + '\n';
2016-07-20 12:21:00 +02:00
}
Tokenizer tokenizer2(&mSettings, this);
2016-07-28 15:35:41 +02:00
std::istringstream istr2(code);
tokenizer2.list.createTokens(istr2);
executeRules("define", tokenizer2);
}
2010-04-13 19:25:08 +02:00
if (!mSettings.force && configurations.size() > mSettings.maxConfigs) {
if (mSettings.severity.isEnabled(Severity::information)) {
tooManyConfigsError(Path::toNativeSeparators(filename),configurations.size());
} else {
2018-06-17 19:20:07 +02:00
mTooManyConfigs = true;
}
}
std::set<unsigned long long> hashes;
2020-12-27 21:05:31 +01:00
int checkCount = 0;
bool hasValidConfig = false;
std::list<std::string> configurationError;
2018-11-05 09:49:28 +01:00
for (const std::string &currCfg : configurations) {
// bail out if terminated
if (Settings::terminated())
break;
// Check only a few configurations (default 12), after that bail out, unless --force
// was used.
if (!mSettings.force && ++checkCount > mSettings.maxConfigs)
break;
2010-04-13 19:25:08 +02:00
if (!mSettings.userDefines.empty()) {
2019-02-03 19:49:27 +01:00
mCurrentConfig = mSettings.userDefines;
const std::vector<std::string> v1(split(mSettings.userDefines, ";"));
for (const std::string &cfg: split(currCfg, ";")) {
if (std::find(v1.cbegin(), v1.cend(), cfg) == v1.cend()) {
2019-02-03 19:49:27 +01:00
mCurrentConfig += ";" + cfg;
}
}
} else {
mCurrentConfig = currCfg;
}
if (mSettings.preprocessOnly) {
Timer t("Preprocessor::getcode", mSettings.showtime, &s_timerResults);
2018-06-17 07:40:13 +02:00
std::string codeWithoutCfg = preprocessor.getcode(tokens1, mCurrentConfig, files, true);
t.stop();
2016-01-02 11:48:36 +01:00
if (codeWithoutCfg.compare(0,5,"#file") == 0)
codeWithoutCfg.insert(0U, "//");
std::string::size_type pos = 0;
while ((pos = codeWithoutCfg.find("\n#file",pos)) != std::string::npos)
codeWithoutCfg.insert(pos+1U, "//");
pos = 0;
while ((pos = codeWithoutCfg.find("\n#endfile",pos)) != std::string::npos)
codeWithoutCfg.insert(pos+1U, "//");
pos = 0;
while ((pos = codeWithoutCfg.find(Preprocessor::macroChar,pos)) != std::string::npos)
codeWithoutCfg[pos] = ' ';
reportOut(codeWithoutCfg);
continue;
}
Tokenizer tokenizer(&mSettings, this, &preprocessor);
2019-07-31 22:35:51 +02:00
if (mSettings.showtime != SHOWTIME_MODES::SHOWTIME_NONE)
tokenizer.setTimerResults(&s_timerResults);
try {
// Create tokens, skip rest of iteration if failed
{
Timer timer("Tokenizer::createTokens", mSettings.showtime, &s_timerResults);
simplecpp::TokenList tokensP = preprocessor.preprocess(tokens1, mCurrentConfig, files, true);
tokenizer.createTokens(std::move(tokensP));
}
hasValidConfig = true;
// If only errors are printed, print filename after the check
2018-11-05 09:49:28 +01:00
if (!mSettings.quiet && (!mCurrentConfig.empty() || checkCount > 1)) {
std::string fixedpath = Path::simplifyPath(filename);
fixedpath = Path::toNativeSeparators(fixedpath);
2021-07-08 21:21:35 +02:00
mErrorLogger.reportOut("Checking " + fixedpath + ": " + mCurrentConfig + "...", Color::FgGreen);
}
if (!tokenizer.tokens())
continue;
// skip rest of iteration if just checking configuration
if (mSettings.checkConfiguration)
continue;
// Check raw tokens
checkRawTokens(tokenizer);
// Simplify tokens into normal form, skip rest of iteration if failed
2023-04-11 19:52:55 +02:00
if (!tokenizer.simplifyTokens1(mCurrentConfig))
continue;
// dump xml if --dump
if ((mSettings.dump || !mSettings.addons.empty()) && fdump.is_open()) {
2018-06-17 07:40:13 +02:00
fdump << "<dump cfg=\"" << ErrorLogger::toxml(mCurrentConfig) << "\">" << std::endl;
fdump << " <standards>" << std::endl;
fdump << " <c version=\"" << mSettings.standards.getC() << "\"/>" << std::endl;
fdump << " <cpp version=\"" << mSettings.standards.getCPP() << "\"/>" << std::endl;
fdump << " </standards>" << std::endl;
preprocessor.dump(fdump);
tokenizer.dump(fdump);
2015-12-14 20:05:17 +01:00
fdump << "</dump>" << std::endl;
}
// Need to call this even if the hash will skip this configuration
mSettings.nomsg.markUnmatchedInlineSuppressionsAsChecked(tokenizer);
// Skip if we already met the same simplified token list
if (mSettings.force || mSettings.maxConfigs > 1) {
const std::size_t hash = tokenizer.list.calculateHash();
if (hashes.find(hash) != hashes.end()) {
2018-08-05 11:19:20 +02:00
if (mSettings.debugwarnings)
2018-06-17 07:40:13 +02:00
purgedConfigurationMessage(filename, mCurrentConfig);
continue;
}
hashes.insert(hash);
}
// Check normal tokens
checkNormalTokens(tokenizer);
// Analyze info..
if (!mSettings.buildDir.empty())
checkUnusedFunctions.parseTokens(tokenizer, filename.c_str(), &mSettings);
// handling of "simple" rules has been removed.
// cppcheck-suppress knownConditionTrueFalse
2022-07-18 16:20:17 +02:00
if (mSimplify && hasRule("simple"))
throw InternalError(nullptr, "Handling of \"simple\" rules has been removed in Cppcheck. Use --addon instead.");
} catch (const simplecpp::Output &o) {
// #error etc during preprocessing
2018-06-17 07:40:13 +02:00
configurationError.push_back((mCurrentConfig.empty() ? "\'\'" : mCurrentConfig) + " : [" + o.location.file() + ':' + MathLib::toString(o.location.line) + "] " + o.msg);
--checkCount; // don't count invalid configurations
continue;
} catch (const TerminateException &) {
// Analysis is terminated
return mExitCode;
} catch (const InternalError &e) {
2020-05-23 07:16:49 +02:00
std::list<ErrorMessage::FileLocation> locationList;
if (e.token) {
ErrorMessage::FileLocation loc(e.token, &tokenizer.list);
locationList.push_back(std::move(loc));
} else {
2020-05-23 07:16:49 +02:00
ErrorMessage::FileLocation loc2(filename, 0, 0);
locationList.push_back(std::move(loc2));
if (filename != tokenizer.list.getSourceFilePath()) {
ErrorMessage::FileLocation loc(tokenizer.list.getSourceFilePath(), 0, 0);
locationList.push_back(std::move(loc));
}
}
ErrorMessage errmsg(std::move(locationList),
tokenizer.list.getSourceFilePath(),
2020-05-23 07:16:49 +02:00
Severity::error,
e.errorMessage,
e.id,
Certainty::normal);
reportErr(errmsg);
}
2010-04-13 19:25:08 +02:00
}
if (!hasValidConfig && configurations.size() > 1 && mSettings.severity.isEnabled(Severity::information)) {
std::string msg;
msg = "This file is not analyzed. Cppcheck failed to extract a valid configuration. Use -v for more details.";
msg += "\nThis file is not analyzed. Cppcheck failed to extract a valid configuration. The tested configurations have these preprocessor errors:";
for (const std::string &s : configurationError)
msg += '\n' + s;
const std::string locFile = Path::toNativeSeparators(filename);
2020-05-23 07:16:49 +02:00
ErrorMessage::FileLocation loc;
loc.setfile(locFile);
ErrorMessage errmsg({std::move(loc)},
locFile,
2020-05-23 07:16:49 +02:00
Severity::information,
msg,
"noValidConfiguration",
Certainty::normal);
reportErr(errmsg);
}
// dumped all configs, close root </dumps> element now
if (fdump.is_open()) {
fdump << "</dumps>" << std::endl;
fdump.close();
}
executeAddons(dumpFile);
} catch (const TerminateException &) {
// Analysis is terminated
return mExitCode;
} catch (const std::runtime_error &e) {
internalError(filename, e.what());
2016-05-28 13:24:19 +02:00
} catch (const std::bad_alloc &e) {
2016-05-30 09:17:40 +02:00
internalError(filename, e.what());
} catch (const InternalError &e) {
internalError(filename, e.errorMessage);
2018-06-17 07:43:25 +02:00
mExitCode=1; // e.g. reflect a syntax error
}
if (!mSettings.buildDir.empty()) {
mAnalyzerInformation.setFileInfo("CheckUnusedFunctions", checkUnusedFunctions.analyzerInfo());
mAnalyzerInformation.close();
}
2016-10-29 12:18:11 +02:00
// In jointSuppressionReport mode, unmatched suppressions are
// collected after all files are processed
if (!mSettings.useSingleJob() && (mSettings.severity.isEnabled(Severity::information) || mSettings.checkConfiguration)) {
reportUnmatchedSuppressions(mSettings.nomsg.getUnmatchedLocalSuppressions(filename, isUnusedFunctionCheckEnabled()));
}
2010-04-13 19:25:08 +02:00
2018-06-16 23:31:16 +02:00
mErrorList.clear();
2018-06-17 07:43:25 +02:00
return mExitCode;
}
void CppCheck::internalError(const std::string &filename, const std::string &msg)
{
const std::string fixedpath = Path::toNativeSeparators(filename);
2015-01-12 23:09:17 +01:00
const std::string fullmsg("Bailing out from checking " + fixedpath + " since there was an internal error: " + msg);
if (mSettings.severity.isEnabled(Severity::information)) {
2020-05-23 07:16:49 +02:00
const ErrorMessage::FileLocation loc1(filename, 0, 0);
std::list<ErrorMessage::FileLocation> callstack(1, loc1);
2020-05-23 07:16:49 +02:00
ErrorMessage errmsg(callstack,
emptyString,
Severity::information,
fullmsg,
"internalError",
Certainty::normal);
mErrorLogger.reportErr(errmsg);
} else {
// Report on stdout
mErrorLogger.reportOut(fullmsg);
}
}
2010-04-13 19:25:08 +02:00
//---------------------------------------------------------------------------
// CppCheck - A function that checks a raw token list
2010-04-13 19:25:08 +02:00
//---------------------------------------------------------------------------
void CppCheck::checkRawTokens(const Tokenizer &tokenizer)
2010-04-13 19:25:08 +02:00
{
// Execute rules for "raw" code
executeRules("raw", tokenizer);
}
2010-04-13 19:25:08 +02:00
//---------------------------------------------------------------------------
// CppCheck - A function that checks a normal token list
//---------------------------------------------------------------------------
void CppCheck::checkNormalTokens(const Tokenizer &tokenizer)
{
// TODO: this should actually be the behavior if only "--enable=unusedFunction" is specified - see #10648
const char* unusedFunctionOnly = std::getenv("UNUSEDFUNCTION_ONLY");
const bool doUnusedFunctionOnly = unusedFunctionOnly && (std::strcmp(unusedFunctionOnly, "1") == 0);
const std::time_t maxTime = mSettings.checksMaxTime > 0 ? std::time(nullptr) + mSettings.checksMaxTime : 0;
// call all "runChecks" in all registered Check classes
for (Check *check : Check::instances()) {
if (Settings::terminated())
return;
2016-02-11 16:10:52 +01:00
if (maxTime > 0 && std::time(nullptr) > maxTime) {
if (mSettings.debugwarnings) {
ErrorMessage::FileLocation loc;
loc.setfile(tokenizer.list.getFiles()[0]);
ErrorMessage errmsg({std::move(loc)},
emptyString,
Severity::debug,
"Checks maximum time exceeded",
"checksMaxTime",
Certainty::normal);
reportErr(errmsg);
}
return;
}
if (doUnusedFunctionOnly && dynamic_cast<CheckUnusedFunctions*>(check) == nullptr)
continue;
Timer timerRunChecks(check->name() + "::runChecks", mSettings.showtime, &s_timerResults);
check->runChecks(&tokenizer, &mSettings, this);
}
if (mSettings.clang)
// TODO: Use CTU for Clang analysis
return;
2018-12-25 21:11:23 +01:00
if (mSettings.useSingleJob() || !mSettings.buildDir.empty()) {
// Analyse the tokens..
CTU::FileInfo *fi1 = CTU::getFileInfo(&tokenizer);
if (fi1) {
if (mSettings.useSingleJob())
mFileInfo.push_back(fi1);
if (!mSettings.buildDir.empty())
mAnalyzerInformation.setFileInfo("ctu", fi1->toString());
}
2018-12-25 21:11:23 +01:00
for (const Check *check : Check::instances()) {
if (doUnusedFunctionOnly && dynamic_cast<const CheckUnusedFunctions*>(check) == nullptr)
continue;
Check::FileInfo *fi = check->getFileInfo(&tokenizer, &mSettings);
if (fi != nullptr) {
if (mSettings.useSingleJob())
mFileInfo.push_back(fi);
if (!mSettings.buildDir.empty())
mAnalyzerInformation.setFileInfo(check->name(), fi->toString());
}
2016-10-29 12:18:11 +02:00
}
2020-01-14 21:17:07 +01:00
}
executeRules("normal", tokenizer);
}
2010-04-13 19:25:08 +02:00
//---------------------------------------------------------------------------
2010-12-12 11:56:22 +01:00
2019-03-17 08:19:56 +01:00
bool CppCheck::hasRule(const std::string &tokenlist) const
{
2019-03-17 08:19:56 +01:00
#ifdef HAVE_RULES
for (const Settings::Rule &rule : mSettings.rules) {
if (rule.tokenlist == tokenlist)
return true;
}
#else
(void)tokenlist;
#endif
return false;
2010-04-13 19:25:08 +02:00
}
2019-03-17 08:19:56 +01:00
#ifdef HAVE_RULES
static const char * pcreErrorCodeToString(const int pcreExecRet)
{
switch (pcreExecRet) {
case PCRE_ERROR_NULL:
return "Either code or subject was passed as NULL, or ovector was NULL "
"and ovecsize was not zero (PCRE_ERROR_NULL)";
case PCRE_ERROR_BADOPTION:
return "An unrecognized bit was set in the options argument (PCRE_ERROR_BADOPTION)";
case PCRE_ERROR_BADMAGIC:
return "PCRE stores a 4-byte \"magic number\" at the start of the compiled code, "
"to catch the case when it is passed a junk pointer and to detect when a "
"pattern that was compiled in an environment of one endianness is run in "
"an environment with the other endianness. This is the error that PCRE "
"gives when the magic number is not present (PCRE_ERROR_BADMAGIC)";
case PCRE_ERROR_UNKNOWN_NODE:
return "While running the pattern match, an unknown item was encountered in the "
"compiled pattern. This error could be caused by a bug in PCRE or by "
"overwriting of the compiled pattern (PCRE_ERROR_UNKNOWN_NODE)";
case PCRE_ERROR_NOMEMORY:
return "If a pattern contains back references, but the ovector that is passed "
"to pcre_exec() is not big enough to remember the referenced substrings, "
"PCRE gets a block of memory at the start of matching to use for this purpose. "
"If the call via pcre_malloc() fails, this error is given. The memory is "
"automatically freed at the end of matching. This error is also given if "
"pcre_stack_malloc() fails in pcre_exec(). "
"This can happen only when PCRE has been compiled with "
"--disable-stack-for-recursion (PCRE_ERROR_NOMEMORY)";
case PCRE_ERROR_NOSUBSTRING:
return "This error is used by the pcre_copy_substring(), pcre_get_substring(), "
"and pcre_get_substring_list() functions (see below). "
"It is never returned by pcre_exec() (PCRE_ERROR_NOSUBSTRING)";
case PCRE_ERROR_MATCHLIMIT:
return "The backtracking limit, as specified by the match_limit field in a pcre_extra "
"structure (or defaulted) was reached. "
"See the description above (PCRE_ERROR_MATCHLIMIT)";
case PCRE_ERROR_CALLOUT:
return "This error is never generated by pcre_exec() itself. "
"It is provided for use by callout functions that want to yield a distinctive "
"error code. See the pcrecallout documentation for details (PCRE_ERROR_CALLOUT)";
case PCRE_ERROR_BADUTF8:
return "A string that contains an invalid UTF-8 byte sequence was passed as a subject, "
"and the PCRE_NO_UTF8_CHECK option was not set. If the size of the output vector "
"(ovecsize) is at least 2, the byte offset to the start of the the invalid UTF-8 "
"character is placed in the first element, and a reason code is placed in the "
"second element. The reason codes are listed in the following section. For "
"backward compatibility, if PCRE_PARTIAL_HARD is set and the problem is a truncated "
"UTF-8 character at the end of the subject (reason codes 1 to 5), "
"PCRE_ERROR_SHORTUTF8 is returned instead of PCRE_ERROR_BADUTF8";
case PCRE_ERROR_BADUTF8_OFFSET:
return "The UTF-8 byte sequence that was passed as a subject was checked and found to "
"be valid (the PCRE_NO_UTF8_CHECK option was not set), but the value of "
"startoffset did not point to the beginning of a UTF-8 character or the end of "
"the subject (PCRE_ERROR_BADUTF8_OFFSET)";
case PCRE_ERROR_PARTIAL:
return "The subject string did not match, but it did match partially. See the "
"pcrepartial documentation for details of partial matching (PCRE_ERROR_PARTIAL)";
case PCRE_ERROR_BADPARTIAL:
return "This code is no longer in use. It was formerly returned when the PCRE_PARTIAL "
"option was used with a compiled pattern containing items that were not supported "
"for partial matching. From release 8.00 onwards, there are no restrictions on "
"partial matching (PCRE_ERROR_BADPARTIAL)";
case PCRE_ERROR_INTERNAL:
return "An unexpected internal error has occurred. This error could be caused by a bug "
"in PCRE or by overwriting of the compiled pattern (PCRE_ERROR_INTERNAL)";
case PCRE_ERROR_BADCOUNT:
2021-08-07 20:51:18 +02:00
return "This error is given if the value of the ovecsize argument is negative "
"(PCRE_ERROR_BADCOUNT)";
case PCRE_ERROR_RECURSIONLIMIT:
return "The internal recursion limit, as specified by the match_limit_recursion "
"field in a pcre_extra structure (or defaulted) was reached. "
"See the description above (PCRE_ERROR_RECURSIONLIMIT)";
case PCRE_ERROR_DFA_UITEM:
return "PCRE_ERROR_DFA_UITEM";
case PCRE_ERROR_DFA_UCOND:
return "PCRE_ERROR_DFA_UCOND";
case PCRE_ERROR_DFA_WSSIZE:
return "PCRE_ERROR_DFA_WSSIZE";
case PCRE_ERROR_DFA_RECURSE:
return "PCRE_ERROR_DFA_RECURSE";
case PCRE_ERROR_NULLWSLIMIT:
return "PCRE_ERROR_NULLWSLIMIT";
case PCRE_ERROR_BADNEWLINE:
return "An invalid combination of PCRE_NEWLINE_xxx options was "
"given (PCRE_ERROR_BADNEWLINE)";
case PCRE_ERROR_BADOFFSET:
return "The value of startoffset was negative or greater than the length "
"of the subject, that is, the value in length (PCRE_ERROR_BADOFFSET)";
case PCRE_ERROR_SHORTUTF8:
return "This error is returned instead of PCRE_ERROR_BADUTF8 when the subject "
"string ends with a truncated UTF-8 character and the PCRE_PARTIAL_HARD option is set. "
"Information about the failure is returned as for PCRE_ERROR_BADUTF8. "
"It is in fact sufficient to detect this case, but this special error code for "
"PCRE_PARTIAL_HARD precedes the implementation of returned information; "
"it is retained for backwards compatibility (PCRE_ERROR_SHORTUTF8)";
case PCRE_ERROR_RECURSELOOP:
return "This error is returned when pcre_exec() detects a recursion loop "
"within the pattern. Specifically, it means that either the whole pattern "
"or a subpattern has been called recursively for the second time at the same "
"position in the subject string. Some simple patterns that might do this "
"are detected and faulted at compile time, but more complicated cases, "
"in particular mutual recursions between two different subpatterns, "
"cannot be detected until run time (PCRE_ERROR_RECURSELOOP)";
case PCRE_ERROR_JIT_STACKLIMIT:
return "This error is returned when a pattern that was successfully studied "
"using a JIT compile option is being matched, but the memory available "
"for the just-in-time processing stack is not large enough. See the pcrejit "
"documentation for more details (PCRE_ERROR_JIT_STACKLIMIT)";
case PCRE_ERROR_BADMODE:
return "This error is given if a pattern that was compiled by the 8-bit library "
"is passed to a 16-bit or 32-bit library function, or vice versa (PCRE_ERROR_BADMODE)";
case PCRE_ERROR_BADENDIANNESS:
return "This error is given if a pattern that was compiled and saved is reloaded on a "
"host with different endianness. The utility function pcre_pattern_to_host_byte_order() "
"can be used to convert such a pattern so that it runs on the new host (PCRE_ERROR_BADENDIANNESS)";
case PCRE_ERROR_DFA_BADRESTART:
return "PCRE_ERROR_DFA_BADRESTART";
2018-09-23 17:04:52 +02:00
#if PCRE_MAJOR >= 8 && PCRE_MINOR >= 32
case PCRE_ERROR_BADLENGTH:
2018-09-23 17:04:52 +02:00
return "This error is given if pcre_exec() is called with a negative value for the length argument (PCRE_ERROR_BADLENGTH)";
case PCRE_ERROR_JIT_BADOPTION:
return "This error is returned when a pattern that was successfully studied using a JIT compile "
"option is being matched, but the matching mode (partial or complete match) does not correspond "
"to any JIT compilation mode. When the JIT fast path function is used, this error may be "
"also given for invalid options. See the pcrejit documentation for more details (PCRE_ERROR_JIT_BADOPTION)";
2018-09-23 17:04:52 +02:00
#endif
}
return "";
}
#endif // HAVE_RULES
void CppCheck::executeRules(const std::string &tokenlist, const Tokenizer &tokenizer)
{
2013-06-12 06:43:52 +02:00
(void)tokenlist;
(void)tokenizer;
2013-06-09 20:17:26 +02:00
#ifdef HAVE_RULES
// There is no rule to execute
2019-03-17 08:19:56 +01:00
if (!hasRule(tokenlist))
return;
// Write all tokens in a string that can be parsed by pcre
std::ostringstream ostr;
for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next())
ostr << " " << tok->str();
const std::string str(ostr.str());
2019-03-17 08:19:56 +01:00
for (const Settings::Rule &rule : mSettings.rules) {
if (rule.pattern.empty() || rule.id.empty() || rule.severity == Severity::none || rule.tokenlist != tokenlist)
continue;
if (!mSettings.quiet) {
2021-07-08 21:21:35 +02:00
reportOut("Processing rule: " + rule.pattern, Color::FgGreen);
}
const char *pcreCompileErrorStr = nullptr;
int erroffset = 0;
pcre * const re = pcre_compile(rule.pattern.c_str(),0,&pcreCompileErrorStr,&erroffset,nullptr);
if (!re) {
if (pcreCompileErrorStr) {
const std::string msg = "pcre_compile failed: " + std::string(pcreCompileErrorStr);
2020-05-23 07:16:49 +02:00
const ErrorMessage errmsg(std::list<ErrorMessage::FileLocation>(),
2020-05-23 07:30:22 +02:00
emptyString,
Severity::error,
msg,
"pcre_compile",
Certainty::normal);
reportErr(errmsg);
}
continue;
}
// Optimize the regex, but only if PCRE_CONFIG_JIT is available
#ifdef PCRE_CONFIG_JIT
const char *pcreStudyErrorStr = nullptr;
pcre_extra * const pcreExtra = pcre_study(re, PCRE_STUDY_JIT_COMPILE, &pcreStudyErrorStr);
// pcre_study() returns NULL for both errors and when it can not optimize the regex.
// The last argument is how one checks for errors.
// It is NULL if everything works, and points to an error string otherwise.
if (pcreStudyErrorStr) {
const std::string msg = "pcre_study failed: " + std::string(pcreStudyErrorStr);
2020-05-23 07:16:49 +02:00
const ErrorMessage errmsg(std::list<ErrorMessage::FileLocation>(),
2020-05-23 07:30:22 +02:00
emptyString,
Severity::error,
msg,
"pcre_study",
Certainty::normal);
reportErr(errmsg);
// pcre_compile() worked, but pcre_study() returned an error. Free the resources allocated by pcre_compile().
pcre_free(re);
continue;
}
#else
const pcre_extra * const pcreExtra = nullptr;
#endif
int pos = 0;
int ovector[30]= {0};
while (pos < (int)str.size()) {
const int pcreExecRet = pcre_exec(re, pcreExtra, str.c_str(), (int)str.size(), pos, 0, ovector, 30);
if (pcreExecRet < 0) {
const std::string errorMessage = pcreErrorCodeToString(pcreExecRet);
if (!errorMessage.empty()) {
2020-05-23 07:16:49 +02:00
const ErrorMessage errmsg(std::list<ErrorMessage::FileLocation>(),
2020-05-23 07:30:22 +02:00
emptyString,
Severity::error,
std::string("pcre_exec failed: ") + errorMessage,
"pcre_exec",
Certainty::normal);
reportErr(errmsg);
}
break;
}
const unsigned int pos1 = (unsigned int)ovector[0];
const unsigned int pos2 = (unsigned int)ovector[1];
// jump to the end of the match for the next pcre_exec
pos = (int)pos2;
// determine location..
2020-05-23 07:16:49 +02:00
ErrorMessage::FileLocation loc;
loc.setfile(tokenizer.list.getSourceFilePath());
loc.line = 0;
std::size_t len = 0;
for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) {
len = len + 1U + tok->str().size();
if (len > pos1) {
loc.setfile(tokenizer.list.getFiles().at(tok->fileIndex()));
loc.line = tok->linenr();
break;
}
}
2020-05-23 07:16:49 +02:00
const std::list<ErrorMessage::FileLocation> callStack(1, loc);
// Create error message
std::string summary;
if (rule.summary.empty())
summary = "found '" + str.substr(pos1, pos2 - pos1) + "'";
else
summary = rule.summary;
const ErrorMessage errmsg(callStack, tokenizer.list.getSourceFilePath(), rule.severity, summary, rule.id, Certainty::normal);
// Report error
reportErr(errmsg);
}
pcre_free(re);
#ifdef PCRE_CONFIG_JIT
// Free up the EXTRA PCRE value (may be NULL at this point)
if (pcreExtra) {
pcre_free_study(pcreExtra);
}
#endif
}
2013-06-09 20:17:26 +02:00
#endif
}
void CppCheck::executeAddons(const std::string& dumpFile)
{
2021-07-07 10:58:13 +02:00
if (!dumpFile.empty()) {
std::vector<std::string> f{dumpFile};
executeAddons(f);
if (!mSettings.dump)
2021-07-07 10:58:13 +02:00
std::remove(dumpFile.c_str());
}
}
2021-07-07 10:58:13 +02:00
void CppCheck::executeAddons(const std::vector<std::string>& files)
{
if (mSettings.addons.empty() || files.empty())
return;
std::string fileList;
if (files.size() >= 2 || endsWith(files[0], ".ctu-info")) {
fileList = Path::getPathFromFilename(files[0]) + FILELIST;
std::ofstream fout(fileList);
for (const std::string& f: files)
fout << f << std::endl;
}
2021-07-07 10:58:13 +02:00
for (const std::string &addon : mSettings.addons) {
struct AddonInfo addonInfo;
const std::string &failedToGetAddonInfo = addonInfo.getAddonInfo(addon, mSettings.exename);
if (!failedToGetAddonInfo.empty()) {
2021-07-08 21:21:35 +02:00
reportOut(failedToGetAddonInfo, Color::FgRed);
2021-07-07 10:58:13 +02:00
mExitCode = 1;
continue;
}
if (addonInfo.name != "misra" && !addonInfo.ctu && endsWith(files.back(), ".ctu-info"))
2021-07-07 10:58:13 +02:00
continue;
2021-07-07 10:58:13 +02:00
const std::string results =
2022-08-22 21:11:28 +02:00
executeAddon(addonInfo, mSettings.addonPython, fileList.empty() ? files[0] : fileList, mSettings.premiumArgs, mExecuteCommand);
2021-07-07 10:58:13 +02:00
std::istringstream istr(results);
std::string line;
2021-07-07 10:58:13 +02:00
while (std::getline(istr, line)) {
if (line.compare(0,1,"{") != 0)
continue;
2021-07-07 10:58:13 +02:00
picojson::value res;
std::istringstream istr2(line);
istr2 >> res;
if (!res.is<picojson::object>())
continue;
2021-07-07 10:58:13 +02:00
picojson::object obj = res.get<picojson::object>();
2021-07-07 10:58:13 +02:00
ErrorMessage errmsg;
if (obj.count("file") > 0) {
std::string fileName = obj["file"].get<std::string>();
const int64_t lineNumber = obj["linenr"].get<int64_t>();
const int64_t column = obj["column"].get<int64_t>();
errmsg.callStack.emplace_back(std::move(fileName), lineNumber, column);
} else if (obj.count("loc") > 0) {
for (const picojson::value &locvalue: obj["loc"].get<picojson::array>()) {
picojson::object loc = locvalue.get<picojson::object>();
std::string fileName = loc["file"].get<std::string>();
const int64_t lineNumber = loc["linenr"].get<int64_t>();
const int64_t column = loc["column"].get<int64_t>();
const std::string info = loc["info"].get<std::string>();
errmsg.callStack.emplace_back(std::move(fileName), info, lineNumber, column);
}
}
2021-07-07 10:58:13 +02:00
errmsg.id = obj["addon"].get<std::string>() + "-" + obj["errorId"].get<std::string>();
const std::string text = obj["message"].get<std::string>();
errmsg.setmsg(text);
const std::string severity = obj["severity"].get<std::string>();
errmsg.severity = Severity::fromString(severity);
if (errmsg.severity == Severity::SeverityType::none)
continue;
errmsg.file0 = ((files.size() == 1) ? files[0] : "");
2021-07-07 10:58:13 +02:00
reportErr(errmsg);
}
2021-07-07 10:58:13 +02:00
}
if (!fileList.empty())
std::remove(fileList.c_str());
2021-07-07 10:58:13 +02:00
}
void CppCheck::executeAddonsWholeProgram(const std::map<std::string, std::size_t> &files)
{
if (mSettings.addons.empty())
return;
std::vector<std::string> ctuInfoFiles;
for (const auto &f: files) {
2021-07-07 10:58:13 +02:00
const std::string &dumpFileName = getDumpFileName(mSettings, f.first);
ctuInfoFiles.push_back(getCtuInfoFileName(dumpFileName));
}
try {
executeAddons(ctuInfoFiles);
2022-07-07 19:45:47 +02:00
} catch (const InternalError& e) {
internalError("", "Internal error during whole program analysis: " + e.errorMessage);
mExitCode = 1;
}
if (mSettings.buildDir.empty()) {
for (const std::string &f: ctuInfoFiles)
std::remove(f.c_str());
}
}
Settings &CppCheck::settings()
2010-04-13 19:25:08 +02:00
{
return mSettings;
2010-04-13 19:25:08 +02:00
}
2020-12-27 21:05:31 +01:00
void CppCheck::tooManyConfigsError(const std::string &file, const int numberOfConfigurations)
{
if (!mSettings.severity.isEnabled(Severity::information) && !mTooManyConfigs)
return;
2018-06-17 19:20:07 +02:00
mTooManyConfigs = false;
if (mSettings.severity.isEnabled(Severity::information) && file.empty())
return;
2020-05-23 07:16:49 +02:00
std::list<ErrorMessage::FileLocation> loclist;
if (!file.empty()) {
loclist.emplace_back(file);
}
std::ostringstream msg;
msg << "Too many #ifdef configurations - cppcheck only checks " << mSettings.maxConfigs;
if (numberOfConfigurations > mSettings.maxConfigs)
msg << " of " << numberOfConfigurations << " configurations. Use --force to check all configurations.\n";
if (file.empty())
msg << " configurations. Use --force to check all configurations. For more details, use --enable=information.\n";
msg << "The checking of the file will be interrupted because there are too many "
"#ifdef configurations. Checking of all #ifdef configurations can be forced "
"by --force command line option or from GUI preferences. However that may "
"increase the checking time.";
if (file.empty())
msg << " For more details, use --enable=information.";
2020-05-23 07:16:49 +02:00
ErrorMessage errmsg(loclist,
emptyString,
Severity::information,
msg.str(),
"toomanyconfigs", CWE398,
Certainty::normal);
reportErr(errmsg);
}
void CppCheck::purgedConfigurationMessage(const std::string &file, const std::string& configuration)
{
2018-06-17 19:20:07 +02:00
mTooManyConfigs = false;
if (mSettings.severity.isEnabled(Severity::information) && file.empty())
return;
2020-05-23 07:16:49 +02:00
std::list<ErrorMessage::FileLocation> loclist;
if (!file.empty()) {
loclist.emplace_back(file);
}
2020-05-23 07:16:49 +02:00
ErrorMessage errmsg(loclist,
emptyString,
Severity::information,
"The configuration '" + configuration + "' was not checked because its code equals another one.",
"purgedConfiguration",
Certainty::normal);
reportErr(errmsg);
}
2010-04-13 19:25:08 +02:00
//---------------------------------------------------------------------------
2020-05-23 07:16:49 +02:00
void CppCheck::reportErr(const ErrorMessage &msg)
2010-04-13 19:25:08 +02:00
{
if (!mSettings.library.reportErrors(msg.file0))
2013-10-20 14:09:10 +02:00
return;
const std::string errmsg = msg.toString(mSettings.verbose);
if (errmsg.empty())
return;
2010-04-13 19:25:08 +02:00
// Alert only about unique errors
if (std::find(mErrorList.cbegin(), mErrorList.cend(), errmsg) != mErrorList.cend())
return;
if (!mSettings.buildDir.empty())
mAnalyzerInformation.reportErr(msg);
// TODO: only convert if necessary
const Suppressions::ErrorMessage errorMessage = msg.toSuppressionsErrorMessage();
2010-04-13 19:25:08 +02:00
2018-06-17 07:29:07 +02:00
if (mUseGlobalSuppressions) {
if (mSettings.nomsg.isSuppressed(errorMessage)) {
return;
}
} else {
if (mSettings.nomsg.isSuppressedLocal(errorMessage)) {
return;
}
}
2010-04-13 19:25:08 +02:00
2019-05-01 11:54:13 +02:00
if (!mSettings.nofail.isSuppressed(errorMessage) && !mSettings.nomsg.isSuppressed(errorMessage)) {
2018-06-17 07:43:25 +02:00
mExitCode = 1;
2019-05-01 11:54:13 +02:00
}
2010-04-13 19:25:08 +02:00
2018-06-16 23:31:16 +02:00
mErrorList.push_back(errmsg);
mErrorLogger.reportErr(msg);
// check if plistOutput should be populated and the current output file is open and the error is not suppressed
if (!mSettings.plistOutput.empty() && mPlistFile.is_open() && !mSettings.nomsg.isSuppressed(errorMessage)) {
// add error to plist output file
mPlistFile << ErrorLogger::plistData(msg);
2017-05-16 14:07:23 +02:00
}
2010-04-13 19:25:08 +02:00
}
2021-07-08 21:21:35 +02:00
void CppCheck::reportOut(const std::string &outmsg, Color c)
2010-04-13 19:25:08 +02:00
{
2021-07-08 21:21:35 +02:00
mErrorLogger.reportOut(outmsg, c);
2010-04-13 19:25:08 +02:00
}
void CppCheck::reportProgress(const std::string &filename, const char stage[], const std::size_t value)
2010-04-13 19:25:08 +02:00
{
mErrorLogger.reportProgress(filename, stage, value);
2010-04-13 19:25:08 +02:00
}
void CppCheck::getErrorMessages()
{
Settings s(mSettings);
s.severity.enable(Severity::warning);
s.severity.enable(Severity::style);
s.severity.enable(Severity::portability);
s.severity.enable(Severity::performance);
s.severity.enable(Severity::information);
purgedConfigurationMessage(emptyString,emptyString);
2018-06-17 19:20:07 +02:00
mTooManyConfigs = true;
tooManyConfigsError(emptyString,0U);
2010-04-13 19:25:08 +02:00
// call all "getErrorMessages" in all registered Check classes
for (std::list<Check *>::const_iterator it = Check::instances().cbegin(); it != Check::instances().cend(); ++it)
(*it)->getErrorMessages(this, &s);
2010-04-13 19:25:08 +02:00
Preprocessor::getErrorMessages(this, &s);
2010-04-13 19:25:08 +02:00
}
2020-01-31 14:13:52 +01:00
void CppCheck::analyseClangTidy(const ImportProject::FileSettings &fileSettings)
2020-01-30 07:14:17 +01:00
{
std::string allIncludes;
2020-01-30 07:14:17 +01:00
for (const std::string &inc : fileSettings.includePaths) {
allIncludes = allIncludes + "-I\"" + inc + "\" ";
}
const std::string allDefines = getDefinesFlags(fileSettings.defines);
2020-01-30 07:14:17 +01:00
#ifdef _WIN32
const char exe[] = "clang-tidy.exe";
#else
const char exe[] = "clang-tidy";
#endif
const std::string args = "-quiet -checks=*,-clang-analyzer-*,-llvm* \"" + fileSettings.filename + "\" -- " + allIncludes + allDefines;
std::string output;
if (!mExecuteCommand(exe, split(args), emptyString, output)) {
std::cerr << "Failed to execute '" << exe << "'" << std::endl;
2020-01-31 14:13:52 +01:00
return;
2020-01-30 07:14:17 +01:00
}
// parse output and create error messages
std::istringstream istr(output);
2020-01-30 07:14:17 +01:00
std::string line;
2020-01-31 14:13:52 +01:00
if (!mSettings.buildDir.empty()) {
const std::string analyzerInfoFile = AnalyzerInformation::getAnalyzerInfoFile(mSettings.buildDir, fileSettings.filename, emptyString);
2020-01-30 07:14:17 +01:00
std::ofstream fcmd(analyzerInfoFile + ".clang-tidy-cmd");
fcmd << istr.str();
}
while (std::getline(istr, line)) {
if (line.find("error") == std::string::npos && line.find("warning") == std::string::npos)
continue;
std::size_t endColumnPos = line.find(": error:");
if (endColumnPos == std::string::npos) {
endColumnPos = line.find(": warning:");
}
const std::size_t endLinePos = line.rfind(':', endColumnPos-1);
const std::size_t endNamePos = line.rfind(':', endLinePos - 1);
2020-01-30 07:14:17 +01:00
const std::size_t endMsgTypePos = line.find(':', endColumnPos + 2);
const std::size_t endErrorPos = line.rfind('[', std::string::npos);
2020-01-31 14:13:52 +01:00
if (endLinePos==std::string::npos || endNamePos==std::string::npos || endMsgTypePos==std::string::npos || endErrorPos==std::string::npos)
2020-01-31 14:10:27 +01:00
continue;
2020-01-31 14:13:52 +01:00
2020-01-30 07:14:17 +01:00
const std::string lineNumString = line.substr(endNamePos + 1, endLinePos - endNamePos - 1);
const std::string columnNumString = line.substr(endLinePos + 1, endColumnPos - endLinePos - 1);
const std::string messageString = line.substr(endMsgTypePos + 1, endErrorPos - endMsgTypePos - 1);
const std::string errorString = line.substr(endErrorPos, line.length());
std::string fixedpath = Path::simplifyPath(line.substr(0, endNamePos));
const int64_t lineNumber = strToInt<int64_t>(lineNumString);
const int64_t column = strToInt<int64_t>(columnNumString);
2020-01-30 07:14:17 +01:00
fixedpath = Path::toNativeSeparators(fixedpath);
2020-05-23 07:16:49 +02:00
ErrorMessage errmsg;
errmsg.callStack.emplace_back(fixedpath, lineNumber, column);
2020-01-30 07:14:17 +01:00
errmsg.id = "clang-tidy-" + errorString.substr(1, errorString.length() - 2);
if (errmsg.id.find("performance") != std::string::npos)
errmsg.severity = Severity::SeverityType::performance;
else if (errmsg.id.find("portability") != std::string::npos)
errmsg.severity = Severity::SeverityType::portability;
else if (errmsg.id.find("cert") != std::string::npos || errmsg.id.find("misc") != std::string::npos || errmsg.id.find("unused") != std::string::npos)
errmsg.severity = Severity::SeverityType::warning;
else
errmsg.severity = Severity::SeverityType::style;
errmsg.file0 = fixedpath;
errmsg.setmsg(messageString);
reportErr(errmsg);
}
}
bool CppCheck::analyseWholeProgram()
{
bool errors = false;
2019-02-03 17:51:02 +01:00
// Init CTU
CTU::maxCtuDepth = mSettings.maxCtuDepth;
// Analyse the tokens
2018-12-25 21:11:23 +01:00
CTU::FileInfo ctu;
for (const Check::FileInfo *fi : mFileInfo) {
const CTU::FileInfo *fi2 = dynamic_cast<const CTU::FileInfo *>(fi);
if (fi2) {
ctu.functionCalls.insert(ctu.functionCalls.end(), fi2->functionCalls.cbegin(), fi2->functionCalls.cend());
ctu.nestedCalls.insert(ctu.nestedCalls.end(), fi2->nestedCalls.cbegin(), fi2->nestedCalls.cend());
2018-12-25 21:11:23 +01:00
}
}
for (Check *check : Check::instances())
errors |= check->analyseWholeProgram(&ctu, mFileInfo, mSettings, *this); // TODO: ctu
2018-06-17 07:43:25 +02:00
return errors && (mExitCode > 0);
}
void CppCheck::analyseWholeProgram(const std::string &buildDir, const std::map<std::string, std::size_t> &files)
{
2021-07-07 10:58:13 +02:00
executeAddonsWholeProgram(files);
if (buildDir.empty()) {
removeCtuInfoFiles(files);
return;
}
if (mSettings.checks.isEnabled(Checks::unusedFunction))
CheckUnusedFunctions::analyseWholeProgram(mSettings, this, buildDir);
std::list<Check::FileInfo*> fileInfoList;
2018-12-25 21:11:23 +01:00
CTU::FileInfo ctuFileInfo;
// Load all analyzer info data..
const std::string filesTxt(buildDir + "/files.txt");
std::ifstream fin(filesTxt);
std::string filesTxtLine;
while (std::getline(fin, filesTxtLine)) {
const std::string::size_type firstColon = filesTxtLine.find(':');
if (firstColon == std::string::npos)
continue;
const std::string::size_type lastColon = filesTxtLine.rfind(':');
if (firstColon == lastColon)
continue;
const std::string xmlfile = buildDir + '/' + filesTxtLine.substr(0,firstColon);
2017-03-30 11:20:04 +02:00
//const std::string sourcefile = filesTxtLine.substr(lastColon+1);
tinyxml2::XMLDocument doc;
const tinyxml2::XMLError error = doc.LoadFile(xmlfile.c_str());
if (error != tinyxml2::XML_SUCCESS)
continue;
const tinyxml2::XMLElement * const rootNode = doc.FirstChildElement();
if (rootNode == nullptr)
continue;
for (const tinyxml2::XMLElement *e = rootNode->FirstChildElement(); e; e = e->NextSiblingElement()) {
if (std::strcmp(e->Name(), "FileInfo") != 0)
continue;
const char *checkClassAttr = e->Attribute("check");
if (!checkClassAttr)
continue;
2018-12-25 21:11:23 +01:00
if (std::strcmp(checkClassAttr, "ctu") == 0) {
ctuFileInfo.loadFromXml(e);
continue;
}
for (Check *check : Check::instances()) {
if (checkClassAttr == check->name())
fileInfoList.push_back(check->loadFileInfoFromXml(e));
}
}
}
2019-02-03 17:51:02 +01:00
// Set CTU max depth
CTU::maxCtuDepth = mSettings.maxCtuDepth;
// Analyse the tokens
2018-12-25 21:11:23 +01:00
for (Check *check : Check::instances())
check->analyseWholeProgram(&ctuFileInfo, fileInfoList, mSettings, *this);
2018-12-25 21:11:23 +01:00
for (Check::FileInfo *fi : fileInfoList)
delete fi;
}
2016-10-28 12:10:19 +02:00
bool CppCheck::isUnusedFunctionCheckEnabled() const
{
return (mSettings.useSingleJob() && mSettings.checks.isEnabled(Checks::unusedFunction));
}
void CppCheck::removeCtuInfoFiles(const std::map<std::string, std::size_t> &files)
{
if (mSettings.buildDir.empty()) {
for (const auto& f: files) {
const std::string &dumpFileName = getDumpFileName(mSettings, f.first);
const std::string &ctuInfoFileName = getCtuInfoFileName(dumpFileName);
std::remove(ctuInfoFileName.c_str());
}
for (const auto& fs: mSettings.project.fileSettings) {
const std::string &dumpFileName = getDumpFileName(mSettings, fs.filename);
const std::string &ctuInfoFileName = getCtuInfoFileName(dumpFileName);
std::remove(ctuInfoFileName.c_str());
}
}
}