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/>.
|
|
|
|
*/
|
2023-10-08 21:28:57 +02:00
|
|
|
|
2010-04-13 19:25:08 +02:00
|
|
|
#include "cppcheck.h"
|
|
|
|
|
2023-10-08 21:28:57 +02:00
|
|
|
#include "addoninfo.h"
|
2010-04-13 19:25:08 +02:00
|
|
|
#include "check.h"
|
2016-10-29 12:18:11 +02:00
|
|
|
#include "checkunusedfunctions.h"
|
2021-04-30 16:47:02 +02:00
|
|
|
#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"
|
2022-01-27 19:03:20 +01:00
|
|
|
#include "errortypes.h"
|
2023-11-02 17:42:41 +01:00
|
|
|
#include "filesettings.h"
|
2017-05-27 04:33:47 +02:00
|
|
|
#include "library.h"
|
|
|
|
#include "path.h"
|
|
|
|
#include "platform.h"
|
2023-10-09 10:07:20 +02:00
|
|
|
#include "preprocessor.h"
|
2022-01-27 19:03:20 +01:00
|
|
|
#include "standards.h"
|
2017-05-27 04:33:47 +02:00
|
|
|
#include "suppressions.h"
|
2017-03-30 10:14:17 +02:00
|
|
|
#include "timer.h"
|
2022-01-27 19:03:20 +01:00
|
|
|
#include "token.h"
|
2023-10-09 10:07:20 +02:00
|
|
|
#include "tokenize.h"
|
2017-05-27 04:33:47 +02:00
|
|
|
#include "tokenlist.h"
|
2022-01-27 19:03:20 +01:00
|
|
|
#include "utils.h"
|
|
|
|
#include "valueflow.h"
|
2017-03-30 10:14:17 +02:00
|
|
|
#include "version.h"
|
2016-10-29 12:18:11 +02:00
|
|
|
|
2010-04-13 19:25:08 +02:00
|
|
|
#include <algorithm>
|
2023-10-09 10:07:20 +02:00
|
|
|
#include <cassert>
|
2022-01-27 19:03:20 +01:00
|
|
|
#include <cstdio>
|
|
|
|
#include <cstdint>
|
2017-05-27 04:33:47 +02:00
|
|
|
#include <cstring>
|
2022-01-27 19:03:20 +01:00
|
|
|
#include <cctype>
|
|
|
|
#include <cstdlib>
|
2023-03-02 21:50:14 +01:00
|
|
|
#include <ctime>
|
2022-01-27 19:03:20 +01:00
|
|
|
#include <exception>
|
2022-09-24 11:59:13 +02:00
|
|
|
#include <fstream>
|
2022-01-27 19:03:20 +01:00
|
|
|
#include <iostream> // <- TEMPORARY
|
2017-05-27 04:33:47 +02:00
|
|
|
#include <new>
|
|
|
|
#include <set>
|
2022-09-16 07:15:49 +02:00
|
|
|
#include <sstream> // IWYU pragma: keep
|
2010-09-06 21:00:56 +02:00
|
|
|
#include <stdexcept>
|
2022-01-27 19:03:20 +01:00
|
|
|
#include <string>
|
2023-03-02 21:50:14 +01:00
|
|
|
#include <unordered_set>
|
2022-01-27 19:03:20 +01:00
|
|
|
#include <utility>
|
2017-05-27 04:33:47 +02:00
|
|
|
#include <vector>
|
2022-01-27 19:03:20 +01:00
|
|
|
|
2023-02-13 20:54:21 +01:00
|
|
|
#ifndef _WIN32
|
|
|
|
#include <unistd.h>
|
|
|
|
#else
|
|
|
|
#include <process.h>
|
|
|
|
#endif
|
|
|
|
|
2023-08-16 19:35:53 +02:00
|
|
|
#include "json.h"
|
2022-01-27 19:03:20 +01:00
|
|
|
|
|
|
|
#include <simplecpp.h>
|
|
|
|
|
|
|
|
#include <tinyxml2.h>
|
2010-04-13 19:25:08 +02:00
|
|
|
|
2011-02-14 19:37:58 +01:00
|
|
|
#ifdef HAVE_RULES
|
2021-10-24 11:06:48 +02:00
|
|
|
#ifdef _WIN32
|
2010-12-12 11:56:22 +01:00
|
|
|
#define PCRE_STATIC
|
2021-10-24 11:06:48 +02:00
|
|
|
#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
|
|
|
|
2022-01-27 19:03:20 +01:00
|
|
|
class SymbolDatabase;
|
|
|
|
|
2023-11-03 09:55:44 +01:00
|
|
|
static constexpr char Version[] = CPPCHECK_VERSION_STRING;
|
|
|
|
static constexpr char ExtraVersion[] = "";
|
2011-08-11 16:34:59 +02:00
|
|
|
|
2023-11-03 09:55:44 +01:00
|
|
|
static constexpr char FILELIST[] = "cppcheck-addon-ctu-file-list";
|
2021-07-09 08:33:07 +02:00
|
|
|
|
2019-11-20 15:37:09 +01:00
|
|
|
static TimerResults s_timerResults;
|
2010-04-13 19:25:08 +02:00
|
|
|
|
Mapped toomanyconfigs ,AssignmentAddressToInteger
,AssignmentIntegerToAddress ,CastIntegerToAddressAtReturn
,CastAddressToIntegerAtReturn ,assertWithSideEffect ,assignmentInAssert
,uselessAssignmentArg ,uselessAssignmentPtrArg
,comparisonOfFuncReturningBoolError
,comparisonOfTwoFuncsReturningBoolError ,comparisonOfBoolWithBoolError
,incrementboolean ,comparisonOfBoolWithInt ,compareBoolExpressionWithInt
,negativeIndex ,pointerOutOfBounds ,arrayIndexThenCheck
,possibleBufferAccessOutOfBounds ,argumentSize
,arrayIndexOutOfBoundsCond ,noConstructor ,copyCtorPointerCopying
,noCopyConstructor ,uninitMemberVar ,operatorEqVarError
,unusedPrivateFunction ,memsetClassFloat ,mallocOnClassWarning
,operatorEq ,thisSubtraction ,operatorEqRetRefThis ,operatorEqToSelf
,useInitializationList ,duplInheritedMember ,assignIfError
,comparisonError ,multiCondition ,mismatchingBitAnd
,oppositeInnerCondition ,incorrectLogicOperator ,redundantCondition
,moduloAlwaysTrueFalse to their CWEs ids.
2016-02-20 23:56:36 +01:00
|
|
|
// CWE ids used
|
|
|
|
static const CWE CWE398(398U); // Indicator of Poor Code Quality
|
|
|
|
|
2023-08-24 22:47:20 +02:00
|
|
|
// File deleter
|
|
|
|
namespace {
|
|
|
|
class FilesDeleter {
|
|
|
|
public:
|
|
|
|
FilesDeleter() = default;
|
|
|
|
~FilesDeleter() {
|
|
|
|
for (const std::string& fileName: mFilenames)
|
|
|
|
std::remove(fileName.c_str());
|
|
|
|
}
|
|
|
|
void addFile(const std::string& fileName) {
|
|
|
|
mFilenames.push_back(fileName);
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
std::vector<std::string> mFilenames;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2020-05-17 21:45:37 +02:00
|
|
|
static std::string cmdFileName(std::string f)
|
|
|
|
{
|
2020-05-19 08:14:56 +02:00
|
|
|
f = Path::toNativeSeparators(f);
|
2023-02-08 21:01:51 +01:00
|
|
|
if (f.find(' ') != std::string::npos)
|
2020-05-17 21:45:37 +02:00
|
|
|
return "\"" + f + "\"";
|
|
|
|
return f;
|
|
|
|
}
|
|
|
|
|
2020-05-19 16:04:25 +02:00
|
|
|
static std::vector<std::string> split(const std::string &str, const std::string &sep=" ")
|
2020-05-19 08:14:56 +02:00
|
|
|
{
|
2020-05-19 16:04:25 +02:00
|
|
|
std::vector<std::string> ret;
|
|
|
|
for (std::string::size_type startPos = 0U; startPos < str.size();) {
|
2020-08-30 18:15:48 +02:00
|
|
|
startPos = str.find_first_not_of(sep, startPos);
|
|
|
|
if (startPos == std::string::npos)
|
2020-05-19 16:04:25 +02:00
|
|
|
break;
|
2020-08-30 18:15:48 +02:00
|
|
|
|
|
|
|
if (str[startPos] == '\"') {
|
2023-02-08 21:01:51 +01:00
|
|
|
const std::string::size_type endPos = str.find('\"', startPos + 1);
|
2020-08-30 18:15:48 +02:00
|
|
|
ret.push_back(str.substr(startPos + 1, endPos - startPos - 1));
|
|
|
|
startPos = (endPos < str.size()) ? (endPos + 1) : endPos;
|
|
|
|
continue;
|
2020-05-19 16:04:25 +02:00
|
|
|
}
|
2020-08-30 18:15:48 +02:00
|
|
|
|
|
|
|
const std::string::size_type endPos = str.find(sep, startPos + 1);
|
2020-05-19 16:04:25 +02:00
|
|
|
ret.push_back(str.substr(startPos, endPos - startPos));
|
2020-08-30 18:15:48 +02:00
|
|
|
startPos = endPos;
|
2020-05-19 16:04:25 +02:00
|
|
|
}
|
2020-08-30 18:15:48 +02:00
|
|
|
|
2020-05-19 16:04:25 +02:00
|
|
|
return ret;
|
2020-05-19 08:14:56 +02:00
|
|
|
}
|
|
|
|
|
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;
|
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())
|
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";
|
|
|
|
}
|
|
|
|
|
2021-02-06 19:06:05 +01:00
|
|
|
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);
|
2021-02-06 19:06:05 +01:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-08-31 11:54:46 +02:00
|
|
|
fdump << "<?xml version=\"1.0\"?>\n";
|
|
|
|
fdump << "<dumps" << language << ">\n";
|
2021-02-06 19:06:05 +01:00
|
|
|
fdump << " <platform"
|
2023-03-03 18:36:27 +01:00
|
|
|
<< " 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) << '\"'
|
2023-08-31 11:54:46 +02:00
|
|
|
<< "/>" << '\n';
|
2021-02-06 19:06:05 +01:00
|
|
|
}
|
|
|
|
|
2023-09-20 10:40:57 +02:00
|
|
|
static std::string detectPython(const CppCheck::ExecuteCmdFn &executeCommand)
|
|
|
|
{
|
|
|
|
#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) {
|
|
|
|
std::string out;
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
// FIXME: hack to avoid debug assertion with _popen() in executeCommand() for non-existing commands
|
|
|
|
const std::string cmd = std::string(py_exe) + " --version >NUL";
|
|
|
|
if (system(cmd.c_str()) != 0) {
|
|
|
|
// TODO: get more detailed error?
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (executeCommand(py_exe, split("--version"), "2>&1", out) == EXIT_SUCCESS && startsWith(out, "Python ") && std::isdigit(out[7])) {
|
|
|
|
return py_exe;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
2020-04-05 10:57:51 +02:00
|
|
|
{
|
2020-05-19 16:04:25 +02:00
|
|
|
const std::string redirect = "2>&1";
|
|
|
|
|
2020-05-18 07:44:46 +02:00
|
|
|
std::string pythonExe;
|
2020-04-05 10:57:28 +02:00
|
|
|
|
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 {
|
2023-09-20 10:40:57 +02:00
|
|
|
// store in static variable so we only look this up once
|
|
|
|
static const std::string detectedPythonExe = detectPython(executeCommand);
|
|
|
|
if (detectedPythonExe.empty())
|
2020-05-19 08:14:56 +02:00
|
|
|
throw InternalError(nullptr, "Failed to auto detect python");
|
2023-09-20 10:40:57 +02:00
|
|
|
pythonExe = detectedPythonExe;
|
2019-12-17 12:00:01 +01:00
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2023-10-05 11:44:48 +02:00
|
|
|
const bool is_file_list = (file.find(FILELIST) != std::string::npos);
|
|
|
|
const std::string fileArg = (is_file_list ? " --file-list " : " ") + cmdFileName(file);
|
2021-10-11 19:26:51 +02:00
|
|
|
args += fileArg;
|
2021-07-09 08:33:07 +02:00
|
|
|
|
2019-04-14 15:00:03 +02:00
|
|
|
std::string result;
|
2023-09-20 10:40:57 +02:00
|
|
|
if (const int exitcode = executeCommand(pythonExe, split(args), redirect, result)) {
|
|
|
|
std::string message("Failed to execute addon '" + addonInfo.name + "' - exitcode is " + std::to_string(exitcode));
|
|
|
|
std::string details = pythonExe + " " + args;
|
2022-05-18 08:33:04 +02:00
|
|
|
if (result.size() > 2) {
|
2023-09-20 10:40:57 +02:00
|
|
|
details += "\nOutput:\n";
|
|
|
|
details += result;
|
|
|
|
const auto pos = details.find_last_not_of("\n\r");
|
|
|
|
if (pos != std::string::npos)
|
|
|
|
details.resize(pos + 1);
|
2022-05-18 08:33:04 +02:00
|
|
|
}
|
2023-09-20 10:40:57 +02:00
|
|
|
throw InternalError(nullptr, message, details);
|
2022-05-18 08:33:04 +02:00
|
|
|
}
|
2019-12-17 12:00:01 +01:00
|
|
|
|
2023-09-20 10:40:57 +02:00
|
|
|
std::vector<picojson::value> addonResult;
|
|
|
|
|
2019-12-17 12:00:01 +01:00
|
|
|
// Validate output..
|
|
|
|
std::istringstream istr(result);
|
|
|
|
std::string line;
|
|
|
|
while (std::getline(istr, line)) {
|
2023-09-20 10:40:57 +02:00
|
|
|
// TODO: also bail out?
|
|
|
|
if (line.empty()) {
|
|
|
|
//std::cout << "addon '" << addonInfo.name << "' result contains empty line" << std::endl;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: get rid of this
|
|
|
|
if (startsWith(line,"Checking ")) {
|
|
|
|
//std::cout << "addon '" << addonInfo.name << "' result contains 'Checking ' line" << std::endl;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (line[0] != '{') {
|
|
|
|
//std::cout << "addon '" << addonInfo.name << "' result is not a JSON" << std::endl;
|
|
|
|
|
2021-07-05 08:08:15 +02:00
|
|
|
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);
|
2021-07-05 08:08:15 +02:00
|
|
|
}
|
2023-09-20 10:40:57 +02:00
|
|
|
|
|
|
|
//std::cout << "addon '" << addonInfo.name << "' result is " << line << std::endl;
|
|
|
|
|
|
|
|
// TODO: make these failures?
|
|
|
|
picojson::value res;
|
|
|
|
const std::string err = picojson::parse(res, line);
|
|
|
|
if (!err.empty()) {
|
|
|
|
//std::cout << "addon '" << addonInfo.name << "' result is not a valid JSON (" << err << ")" << std::endl;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!res.is<picojson::object>()) {
|
|
|
|
//std::cout << "addon '" << addonInfo.name << "' result is not a JSON object" << std::endl;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
addonResult.emplace_back(std::move(res));
|
2019-12-17 12:00:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Valid results
|
2023-09-20 10:40:57 +02:00
|
|
|
return addonResult;
|
2019-04-14 15:00:03 +02:00
|
|
|
}
|
|
|
|
|
2020-05-20 14:56:55 +02:00
|
|
|
static std::string getDefinesFlags(const std::string &semicolonSeparatedString)
|
|
|
|
{
|
|
|
|
std::string flags;
|
|
|
|
for (const std::string &d: split(semicolonSeparatedString, ";"))
|
|
|
|
flags += "-D" + d + " ";
|
|
|
|
return flags;
|
|
|
|
}
|
|
|
|
|
2020-05-19 16:04:25 +02:00
|
|
|
CppCheck::CppCheck(ErrorLogger &errorLogger,
|
|
|
|
bool useGlobalSuppressions,
|
2023-09-20 10:40:57 +02:00
|
|
|
ExecuteCmdFn executeCommand)
|
2020-05-19 16:04:25 +02:00
|
|
|
: mErrorLogger(errorLogger)
|
|
|
|
, mUseGlobalSuppressions(useGlobalSuppressions)
|
2022-07-28 22:51:45 +02:00
|
|
|
, 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();
|
2014-11-15 10:43:49 +01:00
|
|
|
}
|
2022-09-24 11:59:13 +02:00
|
|
|
|
|
|
|
if (mPlistFile.is_open()) {
|
|
|
|
mPlistFile << ErrorLogger::plistFooter();
|
|
|
|
mPlistFile.close();
|
|
|
|
}
|
2010-04-13 19:25:08 +02:00
|
|
|
}
|
|
|
|
|
2011-04-24 18:10:25 +02:00
|
|
|
const char * CppCheck::version()
|
2010-04-13 19:25:08 +02:00
|
|
|
{
|
2011-08-11 16:34:59 +02:00
|
|
|
return Version;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char * CppCheck::extraVersion()
|
|
|
|
{
|
|
|
|
return ExtraVersion;
|
2010-04-13 19:25:08 +02:00
|
|
|
}
|
|
|
|
|
2023-03-02 22:05:41 +01:00
|
|
|
static bool reportClangErrors(std::istream &is, const std::function<void(const ErrorMessage&)>& reportErr, std::vector<ErrorMessage> &warnings)
|
2021-04-30 16:47:02 +02:00
|
|
|
{
|
|
|
|
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:");
|
2023-03-02 22:05:41 +01:00
|
|
|
if (pos3 == std::string::npos)
|
2021-12-11 12:42:15 +01:00
|
|
|
pos3 = line.find(": warning:");
|
2021-04-30 16:47:02 +02:00
|
|
|
if (pos3 == std::string::npos)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// file:line:column: error: ....
|
2023-02-08 21:01:51 +01:00
|
|
|
const std::string::size_type pos2 = line.rfind(':', pos3 - 1);
|
|
|
|
const std::string::size_type pos1 = line.rfind(':', pos2 - 1);
|
2021-04-30 16:47:02 +02:00
|
|
|
|
|
|
|
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);
|
2023-02-08 21:01:51 +01:00
|
|
|
const std::string msg = line.substr(line.find(':', pos3+1) + 2);
|
2021-04-30 16:47:02 +02:00
|
|
|
|
2022-08-16 22:28:39 +02:00
|
|
|
const std::string locFile = Path::toNativeSeparators(filename);
|
2021-04-30 16:47:02 +02:00
|
|
|
ErrorMessage::FileLocation loc;
|
2022-08-16 22:28:39 +02:00
|
|
|
loc.setfile(locFile);
|
2023-04-08 22:29:09 +02:00
|
|
|
loc.line = strToInt<int>(linenr);
|
|
|
|
loc.column = strToInt<unsigned int>(colnr);
|
2022-08-16 22:28:39 +02:00
|
|
|
ErrorMessage errmsg({std::move(loc)},
|
|
|
|
locFile,
|
2021-04-30 16:47:02 +02:00
|
|
|
Severity::error,
|
|
|
|
msg,
|
|
|
|
"syntaxError",
|
|
|
|
Certainty::normal);
|
2021-12-11 12:42:15 +01:00
|
|
|
|
|
|
|
if (line.compare(pos3, 10, ": warning:") == 0) {
|
2023-03-02 22:05:41 +01:00
|
|
|
warnings.push_back(std::move(errmsg));
|
2021-12-11 12:42:15 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-04-30 16:47:02 +02:00
|
|
|
reportErr(errmsg);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-04-24 18:10:25 +02:00
|
|
|
unsigned int CppCheck::check(const std::string &path)
|
2010-04-13 19:25:08 +02:00
|
|
|
{
|
2021-04-30 16:47:02 +02:00
|
|
|
if (mSettings.clang) {
|
|
|
|
if (!mSettings.quiet)
|
2021-07-08 21:21:35 +02:00
|
|
|
mErrorLogger.reportOut(std::string("Checking ") + path + "...", Color::FgGreen);
|
2021-04-30 16:47:02 +02:00
|
|
|
|
|
|
|
const std::string lang = Path::isCPP(path) ? "-x c++" : "-x c";
|
2022-07-10 10:57:29 +02:00
|
|
|
const std::string analyzerInfo = mSettings.buildDir.empty() ? std::string() : AnalyzerInformation::getAnalyzerInfoFile(mSettings.buildDir, path, emptyString);
|
2021-04-30 16:47:02 +02:00
|
|
|
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;
|
2023-09-20 10:40:57 +02:00
|
|
|
if (mExecuteCommand(exe,split(args2),redirect2,output2) != EXIT_SUCCESS || output2.find("TranslationUnitDecl") == std::string::npos) {
|
2021-04-30 16:47:02 +02:00
|
|
|
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;
|
2021-04-30 16:47:02 +02:00
|
|
|
if (!mSettings.buildDir.empty()) {
|
|
|
|
std::ifstream fin(clangStderr);
|
|
|
|
auto reportError = [this](const ErrorMessage& errorMessage) {
|
|
|
|
reportErr(errorMessage);
|
|
|
|
};
|
2023-03-02 22:05:41 +01:00
|
|
|
if (reportClangErrors(fin, reportError, compilerWarnings))
|
2021-04-30 16:47:02 +02:00
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
std::istringstream istr(output2);
|
|
|
|
auto reportError = [this](const ErrorMessage& errorMessage) {
|
|
|
|
reportErr(errorMessage);
|
|
|
|
};
|
2023-03-02 22:05:41 +01:00
|
|
|
if (reportClangErrors(istr, reportError, compilerWarnings))
|
2021-04-30 16:47:02 +02:00
|
|
|
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);
|
2023-08-04 18:17:27 +02:00
|
|
|
ValueFlow::setValues(tokenizer.list,
|
2023-08-07 18:41:50 +02:00
|
|
|
const_cast<SymbolDatabase&>(*tokenizer.getSymbolDatabase()),
|
2023-04-30 20:39:05 +02:00
|
|
|
this,
|
|
|
|
&mSettings,
|
|
|
|
&s_timerResults);
|
2021-04-30 16:47:02 +02:00
|
|
|
if (mSettings.debugnormal)
|
|
|
|
tokenizer.printDebugOutput(1);
|
|
|
|
checkNormalTokens(tokenizer);
|
|
|
|
|
|
|
|
// create dumpfile
|
|
|
|
std::ofstream fdump;
|
|
|
|
std::string dumpFile;
|
2022-06-12 11:06:15 +02:00
|
|
|
createDumpFile(mSettings, path, fdump, dumpFile);
|
2021-04-30 16:47:02 +02:00
|
|
|
if (fdump.is_open()) {
|
2023-08-31 11:54:46 +02:00
|
|
|
fdump << "<dump cfg=\"\">\n";
|
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";
|
2023-08-31 11:54:46 +02:00
|
|
|
fdump << " <standards>\n";
|
|
|
|
fdump << " <c version=\"" << mSettings.standards.getC() << "\"/>\n";
|
|
|
|
fdump << " <cpp version=\"" << mSettings.standards.getCPP() << "\"/>\n";
|
|
|
|
fdump << " </standards>\n";
|
2021-04-30 16:47:02 +02:00
|
|
|
tokenizer.dump(fdump);
|
2023-08-31 11:54:46 +02:00
|
|
|
fdump << "</dump>\n";
|
|
|
|
fdump << "</dumps>\n";
|
2021-04-30 16:47:02 +02:00
|
|
|
fdump.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
// run addons
|
|
|
|
executeAddons(dumpFile);
|
|
|
|
|
|
|
|
} catch (const InternalError &e) {
|
2023-09-20 10:40:57 +02:00
|
|
|
const ErrorMessage errmsg = ErrorMessage::fromInternalError(e, nullptr, path, "Bailing out from analysis: Processing Clang AST dump failed");
|
|
|
|
reportErr(errmsg);
|
2022-11-12 12:47:54 +01:00
|
|
|
} catch (const TerminateException &) {
|
|
|
|
// Analysis is terminated
|
|
|
|
return mExitCode;
|
2021-04-30 16:47:02 +02:00
|
|
|
} catch (const std::exception &e) {
|
2023-08-31 13:33:29 +02:00
|
|
|
internalError(path, std::string("Processing Clang AST dump failed: ") + e.what());
|
2021-04-30 16:47:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return mExitCode;
|
|
|
|
}
|
|
|
|
|
2023-04-30 20:16:51 +02:00
|
|
|
return checkFile(Path::simplifyPath(path), emptyString);
|
2010-04-13 19:25:08 +02:00
|
|
|
}
|
|
|
|
|
2011-04-24 18:10:25 +02:00
|
|
|
unsigned int CppCheck::check(const std::string &path, const std::string &content)
|
2010-04-13 19:25:08 +02:00
|
|
|
{
|
2014-10-03 10:40:48 +02:00
|
|
|
std::istringstream iss(content);
|
2023-04-30 20:16:51 +02:00
|
|
|
return checkFile(Path::simplifyPath(path), emptyString, &iss);
|
2010-04-13 19:25:08 +02:00
|
|
|
}
|
|
|
|
|
2023-11-02 17:42:41 +01:00
|
|
|
unsigned int CppCheck::check(const FileSettings &fs)
|
2016-08-13 10:50:03 +02:00
|
|
|
{
|
2020-05-19 16:04:25 +02:00
|
|
|
CppCheck temp(mErrorLogger, mUseGlobalSuppressions, mExecuteCommand);
|
2018-06-16 16:10:28 +02:00
|
|
|
temp.mSettings = mSettings;
|
|
|
|
if (!temp.mSettings.userDefines.empty())
|
|
|
|
temp.mSettings.userDefines += ';';
|
2021-04-30 16:47:02 +02:00
|
|
|
if (mSettings.clang)
|
|
|
|
temp.mSettings.userDefines += fs.defines;
|
|
|
|
else
|
|
|
|
temp.mSettings.userDefines += fs.cppcheckDefines();
|
2018-06-16 16:10:28 +02:00
|
|
|
temp.mSettings.includePaths = fs.includePaths;
|
2021-02-28 21:43:51 +01:00
|
|
|
temp.mSettings.userUndefs.insert(fs.undefs.cbegin(), fs.undefs.cend());
|
2020-09-04 15:29:19 +02:00
|
|
|
if (fs.standard.find("++") != std::string::npos)
|
|
|
|
temp.mSettings.standards.setCPP(fs.standard);
|
|
|
|
else if (!fs.standard.empty())
|
|
|
|
temp.mSettings.standards.setC(fs.standard);
|
2023-10-13 16:02:04 +02:00
|
|
|
if (fs.platformType != Platform::Type::Unspecified)
|
2023-03-03 18:36:27 +01:00
|
|
|
temp.mSettings.platform.set(fs.platformType);
|
2021-04-30 16:47:02 +02:00
|
|
|
if (mSettings.clang) {
|
|
|
|
temp.mSettings.includePaths.insert(temp.mSettings.includePaths.end(), fs.systemIncludePaths.cbegin(), fs.systemIncludePaths.cend());
|
|
|
|
return temp.check(Path::simplifyPath(fs.filename));
|
|
|
|
}
|
2023-04-30 20:16:51 +02:00
|
|
|
const unsigned int returnValue = temp.checkFile(Path::simplifyPath(fs.filename), fs.cfg);
|
2020-11-04 21:01:48 +01:00
|
|
|
mSettings.nomsg.addSuppressions(temp.mSettings.nomsg.getSuppressions());
|
|
|
|
return returnValue;
|
2016-08-13 10:50:03 +02:00
|
|
|
}
|
|
|
|
|
2023-04-30 20:16:51 +02:00
|
|
|
static simplecpp::TokenList createTokenList(const std::string& filename, std::vector<std::string>& files, simplecpp::OutputList* outputList, std::istream* fileStream)
|
|
|
|
{
|
|
|
|
if (fileStream)
|
|
|
|
return {*fileStream, files, filename, outputList};
|
|
|
|
|
|
|
|
return {filename, files, outputList};
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
2023-08-24 22:47:20 +02:00
|
|
|
FilesDeleter filesDeleter;
|
|
|
|
|
2019-09-20 21:54:30 +02:00
|
|
|
if (Settings::terminated())
|
2018-06-17 07:43:25 +02:00
|
|
|
return mExitCode;
|
2011-04-24 18:10:25 +02:00
|
|
|
|
2023-05-11 14:04:22 +02:00
|
|
|
const Timer fileTotalTimer(mSettings.showtime == SHOWTIME_MODES::SHOWTIME_FILE_TOTAL, filename);
|
|
|
|
|
2018-06-16 16:10:28 +02:00
|
|
|
if (!mSettings.quiet) {
|
2014-04-02 13:56:34 +02:00
|
|
|
std::string fixedpath = Path::simplifyPath(filename);
|
2011-04-24 18:10:25 +02:00
|
|
|
fixedpath = Path::toNativeSeparators(fixedpath);
|
2021-07-08 21:21:35 +02:00
|
|
|
mErrorLogger.reportOut(std::string("Checking ") + fixedpath + ' ' + cfgname + std::string("..."), Color::FgGreen);
|
2016-08-13 10:50:03 +02:00
|
|
|
|
2018-06-16 16:10:28 +02:00
|
|
|
if (mSettings.verbose) {
|
2018-10-09 15:05:05 +02:00
|
|
|
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);
|
2016-08-13 10:50:03 +02:00
|
|
|
std::string includePaths;
|
2018-06-16 16:10:28 +02:00
|
|
|
for (const std::string &I : mSettings.includePaths)
|
2018-05-22 23:22:46 +02:00
|
|
|
includePaths += " -I" + I;
|
2018-06-16 16:10:28 +02:00
|
|
|
mErrorLogger.reportOut("Includes:" + includePaths);
|
2023-03-03 18:36:27 +01:00
|
|
|
mErrorLogger.reportOut(std::string("Platform:") + mSettings.platform.toString());
|
2016-08-13 10:50:03 +02:00
|
|
|
}
|
2011-04-24 18:10:25 +02:00
|
|
|
}
|
2010-04-13 19:25:08 +02:00
|
|
|
|
2022-09-24 11:59:13 +02:00
|
|
|
if (mPlistFile.is_open()) {
|
|
|
|
mPlistFile << ErrorLogger::plistFooter();
|
|
|
|
mPlistFile.close();
|
2017-05-16 14:07:23 +02:00
|
|
|
}
|
|
|
|
|
2017-08-09 20:00:26 +02:00
|
|
|
CheckUnusedFunctions checkUnusedFunctions(nullptr, nullptr, nullptr);
|
2016-11-07 21:49:58 +01:00
|
|
|
|
2011-10-13 20:53:06 +02:00
|
|
|
try {
|
2023-04-21 10:14:34 +02:00
|
|
|
Preprocessor preprocessor(mSettings, this);
|
2016-07-20 12:21:00 +02:00
|
|
|
std::set<std::string> configurations;
|
|
|
|
|
|
|
|
simplecpp::OutputList outputList;
|
|
|
|
std::vector<std::string> files;
|
2023-04-30 20:16:51 +02:00
|
|
|
simplecpp::TokenList tokens1 = createTokenList(filename, files, &outputList, fileStream);
|
2017-05-28 20:34:58 +02:00
|
|
|
|
|
|
|
// If there is a syntax error, report it and stop
|
2023-04-28 15:37:59 +02:00
|
|
|
const auto output_it = std::find_if(outputList.cbegin(), outputList.cend(), [](const simplecpp::Output &output){
|
|
|
|
return Preprocessor::hasErrors(output);
|
|
|
|
});
|
|
|
|
if (output_it != outputList.cend()) {
|
|
|
|
const simplecpp::Output &output = *output_it;
|
|
|
|
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);
|
|
|
|
std::list<ErrorMessage::FileLocation> callstack(1, loc1);
|
|
|
|
|
|
|
|
ErrorMessage errmsg(callstack,
|
|
|
|
"",
|
|
|
|
Severity::error,
|
|
|
|
output.msg,
|
|
|
|
"syntaxError",
|
|
|
|
Certainty::normal);
|
|
|
|
reportErr(errmsg);
|
|
|
|
return mExitCode;
|
2017-05-28 20:34:58 +02:00
|
|
|
}
|
|
|
|
|
2023-05-03 11:19:13 +02:00
|
|
|
if (mSettings.library.markupFile(filename)) {
|
|
|
|
Tokenizer tokenizer(&mSettings, this, &preprocessor);
|
|
|
|
tokenizer.createTokens(std::move(tokens1));
|
|
|
|
checkUnusedFunctions.getFileInfo(&tokenizer, &mSettings);
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2019-12-09 19:16:55 +01:00
|
|
|
if (!preprocessor.loadFiles(tokens1, files))
|
|
|
|
return mExitCode;
|
2016-07-21 07:48:17 +02:00
|
|
|
|
2018-06-16 16:10:28 +02:00
|
|
|
if (!mSettings.plistOutput.empty()) {
|
2017-05-16 14:07:23 +02:00
|
|
|
std::string filename2;
|
2017-05-27 04:15:54 +02:00
|
|
|
if (filename.find('/') != std::string::npos)
|
|
|
|
filename2 = filename.substr(filename.rfind('/') + 1);
|
2017-05-16 14:07:23 +02:00
|
|
|
else
|
|
|
|
filename2 = filename;
|
2022-10-02 07:12:40 +02:00
|
|
|
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";
|
2022-09-24 11:59:13 +02:00
|
|
|
mPlistFile.open(filename2);
|
|
|
|
mPlistFile << ErrorLogger::plistHeader(version(), files);
|
2017-05-16 14:07:23 +02:00
|
|
|
}
|
|
|
|
|
2023-09-26 14:22:52 +02:00
|
|
|
std::string dumpProlog;
|
2022-06-12 11:06:15 +02:00
|
|
|
if (mSettings.dump || !mSettings.addons.empty()) {
|
2023-09-26 14:22:52 +02:00
|
|
|
dumpProlog += " <rawtokens>\n";
|
|
|
|
for (unsigned int i = 0; i < files.size(); ++i) {
|
|
|
|
dumpProlog += " <file index=\"";
|
|
|
|
dumpProlog += std::to_string(i);
|
|
|
|
dumpProlog += "\" name=\"";
|
|
|
|
dumpProlog += ErrorLogger::toxml(files[i]);
|
|
|
|
dumpProlog += "\"/>\n";
|
|
|
|
}
|
2022-06-12 11:06:15 +02:00
|
|
|
for (const simplecpp::Token *tok = tokens1.cfront(); tok; tok = tok->next) {
|
2023-09-26 14:22:52 +02:00
|
|
|
dumpProlog += " <tok ";
|
|
|
|
|
|
|
|
dumpProlog += "fileIndex=\"";
|
|
|
|
dumpProlog += std::to_string(tok->location.fileIndex);
|
|
|
|
dumpProlog += "\" ";
|
|
|
|
|
|
|
|
dumpProlog += "linenr=\"";
|
|
|
|
dumpProlog += std::to_string(tok->location.line);
|
|
|
|
dumpProlog += "\" ";
|
|
|
|
|
|
|
|
dumpProlog +="column=\"";
|
|
|
|
dumpProlog += std::to_string(tok->location.col);
|
|
|
|
dumpProlog += "\" ";
|
|
|
|
|
|
|
|
dumpProlog += "str=\"";
|
|
|
|
dumpProlog += ErrorLogger::toxml(tok->str());
|
|
|
|
dumpProlog += "\"";
|
|
|
|
|
|
|
|
dumpProlog += "/>\n";
|
2022-06-12 11:06:15 +02:00
|
|
|
}
|
2023-09-26 14:22:52 +02:00
|
|
|
dumpProlog += " </rawtokens>\n";
|
2022-06-12 11:06:15 +02:00
|
|
|
}
|
2017-04-14 10:46:35 +02:00
|
|
|
|
2016-07-21 07:48:17 +02:00
|
|
|
// Parse comments and then remove them
|
2023-04-21 10:14:34 +02:00
|
|
|
preprocessor.inlineSuppressions(tokens1, mSettings.nomsg);
|
2022-06-12 11:06:15 +02:00
|
|
|
if (mSettings.dump || !mSettings.addons.empty()) {
|
2023-09-26 14:22:52 +02:00
|
|
|
std::ostringstream oss;
|
|
|
|
mSettings.nomsg.dump(oss);
|
|
|
|
dumpProlog += oss.str();
|
2018-04-24 22:19:24 +02:00
|
|
|
}
|
2016-07-21 07:48:17 +02:00
|
|
|
tokens1.removeComments();
|
|
|
|
preprocessor.removeComments();
|
2016-07-20 12:21:00 +02:00
|
|
|
|
2018-06-16 16:10:28 +02:00
|
|
|
if (!mSettings.buildDir.empty()) {
|
2016-10-29 22:40:44 +02:00
|
|
|
// Get toolinfo
|
2018-08-09 09:26:54 +02:00
|
|
|
std::ostringstream toolinfo;
|
|
|
|
toolinfo << CPPCHECK_VERSION_STRING;
|
2021-02-24 22:00:06 +01:00
|
|
|
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' : ' ');
|
2018-08-09 09:26:54 +02:00
|
|
|
toolinfo << mSettings.userDefines;
|
|
|
|
mSettings.nomsg.dump(toolinfo);
|
2016-10-29 22:40:44 +02:00
|
|
|
|
2022-07-07 12:16:01 +02:00
|
|
|
// 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;
|
2023-03-02 22:05:41 +01:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-12 11:06:15 +02:00
|
|
|
// write dump file xml prolog
|
|
|
|
std::ofstream fdump;
|
|
|
|
std::string dumpFile;
|
|
|
|
createDumpFile(mSettings, filename, fdump, dumpFile);
|
|
|
|
if (fdump.is_open()) {
|
2023-09-26 14:22:52 +02:00
|
|
|
fdump << dumpProlog;
|
2023-08-24 22:47:20 +02:00
|
|
|
if (!mSettings.dump)
|
|
|
|
filesDeleter.addFile(dumpFile);
|
2022-06-12 11:06:15 +02:00
|
|
|
}
|
|
|
|
|
2016-07-24 14:02:21 +02:00
|
|
|
// Get directives
|
|
|
|
preprocessor.setDirectives(tokens1);
|
2017-05-17 21:58:46 +02:00
|
|
|
preprocessor.simplifyPragmaAsm(&tokens1);
|
2016-07-24 14:02:21 +02:00
|
|
|
|
2016-07-25 14:52:23 +02:00
|
|
|
preprocessor.setPlatformInfo(&tokens1);
|
|
|
|
|
2016-07-20 12:21:00 +02:00
|
|
|
// Get configurations..
|
2019-11-09 17:51:16 +01:00
|
|
|
if ((mSettings.checkAllConfigurations && mSettings.userDefines.empty()) || mSettings.force) {
|
2019-11-20 15:37:09 +01:00
|
|
|
Timer t("Preprocessor::getConfigs", mSettings.showtime, &s_timerResults);
|
2016-07-20 12:21:00 +02:00
|
|
|
configurations = preprocessor.getConfigs(tokens1);
|
|
|
|
} else {
|
2018-06-16 16:10:28 +02:00
|
|
|
configurations.insert(mSettings.userDefines);
|
2010-07-19 14:05:44 +02:00
|
|
|
}
|
2010-04-13 19:25:08 +02:00
|
|
|
|
2018-06-16 16:10:28 +02:00
|
|
|
if (mSettings.checkConfiguration) {
|
2018-05-22 23:22:46 +02:00
|
|
|
for (const std::string &config : configurations)
|
|
|
|
(void)preprocessor.getcode(tokens1, config, files, true);
|
2016-07-21 12:47:00 +02:00
|
|
|
|
2013-08-31 18:38:52 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-05-04 10:54:19 +02:00
|
|
|
#ifdef HAVE_RULES
|
2015-12-11 10:22:06 +01:00
|
|
|
// Run define rules on raw code
|
2023-04-28 15:37:59 +02:00
|
|
|
const auto rules_it = std::find_if(mSettings.rules.cbegin(), mSettings.rules.cend(), [](const Settings::Rule& rule) {
|
2023-03-02 21:48:14 +01:00
|
|
|
return rule.tokenlist == "define";
|
|
|
|
});
|
2023-04-28 15:37:59 +02:00
|
|
|
if (rules_it != mSettings.rules.cend()) {
|
2016-07-28 15:35:41 +02:00
|
|
|
std::string code;
|
2016-07-28 13:40:52 +02:00
|
|
|
const std::list<Directive> &directives = preprocessor.getDirectives();
|
2018-05-22 23:22:46 +02:00
|
|
|
for (const Directive &dir : directives) {
|
2023-09-08 19:30:25 +02:00
|
|
|
if (startsWith(dir.str,"#define ") || startsWith(dir.str,"#include "))
|
2023-08-17 16:46:32 +02:00
|
|
|
code += "#line " + std::to_string(dir.linenr) + " \"" + dir.file + "\"\n" + dir.str + '\n';
|
2016-07-20 12:21:00 +02:00
|
|
|
}
|
2018-06-16 16:10:28 +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);
|
2011-04-24 18:10:25 +02:00
|
|
|
}
|
2023-05-04 10:54:19 +02:00
|
|
|
#endif
|
2010-04-13 19:25:08 +02:00
|
|
|
|
2018-06-16 16:10:28 +02:00
|
|
|
if (!mSettings.force && configurations.size() > mSettings.maxConfigs) {
|
2021-02-24 22:00:06 +01:00
|
|
|
if (mSettings.severity.isEnabled(Severity::information)) {
|
2012-12-26 18:35:49 +01:00
|
|
|
tooManyConfigsError(Path::toNativeSeparators(filename),configurations.size());
|
|
|
|
} else {
|
2018-06-17 19:20:07 +02:00
|
|
|
mTooManyConfigs = true;
|
2012-12-26 18:35:49 +01:00
|
|
|
}
|
2012-05-09 18:54:43 +02:00
|
|
|
}
|
|
|
|
|
2022-07-07 12:16:01 +02:00
|
|
|
std::set<unsigned long long> hashes;
|
2020-12-27 21:05:31 +01:00
|
|
|
int checkCount = 0;
|
2018-05-06 22:02:23 +02:00
|
|
|
bool hasValidConfig = false;
|
2018-05-15 15:42:54 +02:00
|
|
|
std::list<std::string> configurationError;
|
2018-11-05 09:49:28 +01:00
|
|
|
for (const std::string &currCfg : configurations) {
|
2015-12-11 10:22:06 +01:00
|
|
|
// bail out if terminated
|
2019-09-20 21:54:30 +02:00
|
|
|
if (Settings::terminated())
|
2015-12-11 10:22:06 +01:00
|
|
|
break;
|
|
|
|
|
2011-10-05 07:37:43 +02:00
|
|
|
// Check only a few configurations (default 12), after that bail out, unless --force
|
2011-04-24 18:10:25 +02:00
|
|
|
// was used.
|
2018-06-16 16:10:28 +02:00
|
|
|
if (!mSettings.force && ++checkCount > mSettings.maxConfigs)
|
2011-04-24 18:10:25 +02:00
|
|
|
break;
|
2010-04-13 19:25:08 +02:00
|
|
|
|
2018-06-16 16:10:28 +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, ";")) {
|
2022-12-30 15:13:47 +01:00
|
|
|
if (std::find(v1.cbegin(), v1.cend(), cfg) == v1.cend()) {
|
2019-02-03 19:49:27 +01:00
|
|
|
mCurrentConfig += ";" + cfg;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
mCurrentConfig = currCfg;
|
2013-06-08 16:46:54 +02:00
|
|
|
}
|
|
|
|
|
2018-06-16 16:10:28 +02:00
|
|
|
if (mSettings.preprocessOnly) {
|
2019-11-20 15:37:09 +01:00
|
|
|
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);
|
2019-11-20 15:37:09 +01:00
|
|
|
t.stop();
|
2010-04-15 22:45:38 +02:00
|
|
|
|
2023-09-08 19:30:25 +02:00
|
|
|
if (startsWith(codeWithoutCfg,"#file"))
|
2016-01-02 11:48:36 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-03-02 21:48:14 +01:00
|
|
|
Tokenizer tokenizer(&mSettings, this, &preprocessor);
|
2019-07-31 22:35:51 +02:00
|
|
|
if (mSettings.showtime != SHOWTIME_MODES::SHOWTIME_NONE)
|
2020-06-30 10:59:57 +02:00
|
|
|
tokenizer.setTimerResults(&s_timerResults);
|
2015-12-11 10:22:06 +01:00
|
|
|
|
|
|
|
try {
|
|
|
|
// Create tokens, skip rest of iteration if failed
|
2020-05-16 09:34:35 +02:00
|
|
|
{
|
|
|
|
Timer timer("Tokenizer::createTokens", mSettings.showtime, &s_timerResults);
|
|
|
|
simplecpp::TokenList tokensP = preprocessor.preprocess(tokens1, mCurrentConfig, files, true);
|
2020-06-30 10:59:57 +02:00
|
|
|
tokenizer.createTokens(std::move(tokensP));
|
2020-05-16 09:34:35 +02:00
|
|
|
}
|
2018-05-15 15:42:54 +02:00
|
|
|
hasValidConfig = true;
|
2018-05-17 10:27:05 +02:00
|
|
|
|
2023-10-16 19:43:15 +02:00
|
|
|
// locations macros
|
|
|
|
mLocationMacros.clear();
|
|
|
|
for (const Token* tok = tokenizer.tokens(); tok; tok = tok->next()) {
|
|
|
|
if (!tok->getMacroName().empty())
|
|
|
|
mLocationMacros[Location(files[tok->fileIndex()], tok->linenr())].emplace(tok->getMacroName());
|
|
|
|
}
|
|
|
|
|
2018-05-17 10:27:05 +02:00
|
|
|
// 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)) {
|
2018-05-17 10:27:05 +02:00
|
|
|
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);
|
2018-05-17 10:27:05 +02:00
|
|
|
}
|
|
|
|
|
2020-06-30 10:59:57 +02:00
|
|
|
if (!tokenizer.tokens())
|
2017-05-17 22:30:20 +02:00
|
|
|
continue;
|
2015-12-11 10:22:06 +01:00
|
|
|
|
|
|
|
// skip rest of iteration if just checking configuration
|
2018-06-16 16:10:28 +02:00
|
|
|
if (mSettings.checkConfiguration)
|
2015-12-11 10:22:06 +01:00
|
|
|
continue;
|
|
|
|
|
|
|
|
// Check raw tokens
|
2020-06-30 10:59:57 +02:00
|
|
|
checkRawTokens(tokenizer);
|
2015-12-11 10:22:06 +01:00
|
|
|
|
|
|
|
// Simplify tokens into normal form, skip rest of iteration if failed
|
2023-04-11 19:52:55 +02:00
|
|
|
if (!tokenizer.simplifyTokens1(mCurrentConfig))
|
2015-12-11 10:22:06 +01:00
|
|
|
continue;
|
|
|
|
|
2015-12-14 09:37:26 +01:00
|
|
|
// dump xml if --dump
|
2019-04-07 17:01:59 +02:00
|
|
|
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;
|
2019-06-14 12:06:57 +02:00
|
|
|
fdump << " <standards>" << std::endl;
|
|
|
|
fdump << " <c version=\"" << mSettings.standards.getC() << "\"/>" << std::endl;
|
|
|
|
fdump << " <cpp version=\"" << mSettings.standards.getCPP() << "\"/>" << std::endl;
|
|
|
|
fdump << " </standards>" << std::endl;
|
2015-12-07 19:54:41 +01:00
|
|
|
preprocessor.dump(fdump);
|
2020-06-30 10:59:57 +02:00
|
|
|
tokenizer.dump(fdump);
|
2015-12-14 20:05:17 +01:00
|
|
|
fdump << "</dump>" << std::endl;
|
2015-12-11 10:22:06 +01:00
|
|
|
}
|
|
|
|
|
2022-07-07 12:16:01 +02:00
|
|
|
// Need to call this even if the hash will skip this configuration
|
2022-03-30 19:24:53 +02:00
|
|
|
mSettings.nomsg.markUnmatchedInlineSuppressionsAsChecked(tokenizer);
|
|
|
|
|
2015-12-11 10:22:06 +01:00
|
|
|
// Skip if we already met the same simplified token list
|
2018-06-16 16:10:28 +02:00
|
|
|
if (mSettings.force || mSettings.maxConfigs > 1) {
|
2022-07-07 12:16:01 +02:00
|
|
|
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);
|
2015-12-11 10:22:06 +01:00
|
|
|
continue;
|
2016-08-21 12:31:26 +02:00
|
|
|
}
|
2022-07-07 12:16:01 +02:00
|
|
|
hashes.insert(hash);
|
2015-12-11 10:22:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check normal tokens
|
2020-06-30 10:59:57 +02:00
|
|
|
checkNormalTokens(tokenizer);
|
2015-12-11 10:22:06 +01:00
|
|
|
|
2016-11-07 21:49:58 +01:00
|
|
|
// Analyze info..
|
2018-06-16 16:10:28 +02:00
|
|
|
if (!mSettings.buildDir.empty())
|
2020-06-30 10:59:57 +02:00
|
|
|
checkUnusedFunctions.parseTokens(tokenizer, filename.c_str(), &mSettings);
|
2016-11-07 21:49:58 +01:00
|
|
|
|
2023-05-04 10:54:19 +02:00
|
|
|
#ifdef HAVE_RULES
|
2022-06-08 09:58:11 +02:00
|
|
|
// handling of "simple" rules has been removed.
|
2023-08-16 19:35:53 +02:00
|
|
|
if (hasRule("simple"))
|
2022-06-08 09:58:11 +02:00
|
|
|
throw InternalError(nullptr, "Handling of \"simple\" rules has been removed in Cppcheck. Use --addon instead.");
|
2023-05-04 10:54:19 +02:00
|
|
|
#endif
|
2015-12-11 10:22:06 +01:00
|
|
|
|
2018-05-15 15:42:54 +02:00
|
|
|
} catch (const simplecpp::Output &o) {
|
|
|
|
// #error etc during preprocessing
|
2023-08-17 16:46:32 +02:00
|
|
|
configurationError.push_back((mCurrentConfig.empty() ? "\'\'" : mCurrentConfig) + " : [" + o.location.file() + ':' + std::to_string(o.location.line) + "] " + o.msg);
|
2018-05-15 15:42:54 +02:00
|
|
|
--checkCount; // don't count invalid configurations
|
|
|
|
continue;
|
|
|
|
|
2022-11-12 12:47:54 +01:00
|
|
|
} catch (const TerminateException &) {
|
|
|
|
// Analysis is terminated
|
|
|
|
return mExitCode;
|
|
|
|
|
2015-12-11 10:22:06 +01:00
|
|
|
} catch (const InternalError &e) {
|
2023-08-31 13:33:29 +02:00
|
|
|
ErrorMessage errmsg = ErrorMessage::fromInternalError(e, &tokenizer.list, filename);
|
2022-09-07 19:19:04 +02:00
|
|
|
reportErr(errmsg);
|
2011-08-16 21:58:27 +02:00
|
|
|
}
|
2010-04-13 19:25:08 +02:00
|
|
|
}
|
2015-12-11 10:22:06 +01:00
|
|
|
|
2021-02-24 22:00:06 +01:00
|
|
|
if (!hasValidConfig && configurations.size() > 1 && mSettings.severity.isEnabled(Severity::information)) {
|
2018-05-15 15:42:54 +02:00
|
|
|
std::string msg;
|
2018-05-17 10:27:05 +02:00
|
|
|
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:";
|
2018-05-15 15:42:54 +02:00
|
|
|
for (const std::string &s : configurationError)
|
2018-05-17 10:27:05 +02:00
|
|
|
msg += '\n' + s;
|
2018-05-15 15:42:54 +02:00
|
|
|
|
2022-08-16 22:28:39 +02:00
|
|
|
const std::string locFile = Path::toNativeSeparators(filename);
|
2020-05-23 07:16:49 +02:00
|
|
|
ErrorMessage::FileLocation loc;
|
2022-08-16 22:28:39 +02:00
|
|
|
loc.setfile(locFile);
|
|
|
|
ErrorMessage errmsg({std::move(loc)},
|
|
|
|
locFile,
|
2020-05-23 07:16:49 +02:00
|
|
|
Severity::information,
|
|
|
|
msg,
|
|
|
|
"noValidConfiguration",
|
2021-02-24 22:00:06 +01:00
|
|
|
Certainty::normal);
|
2018-05-06 22:02:23 +02:00
|
|
|
reportErr(errmsg);
|
|
|
|
}
|
|
|
|
|
2015-12-14 09:37:26 +01:00
|
|
|
// dumped all configs, close root </dumps> element now
|
2021-02-06 19:06:05 +01:00
|
|
|
if (fdump.is_open()) {
|
2015-12-14 09:37:26 +01:00
|
|
|
fdump << "</dumps>" << std::endl;
|
2019-04-07 17:01:59 +02:00
|
|
|
fdump.close();
|
|
|
|
}
|
|
|
|
|
2021-02-06 19:06:05 +01:00
|
|
|
executeAddons(dumpFile);
|
|
|
|
|
2022-11-12 12:47:54 +01:00
|
|
|
} catch (const TerminateException &) {
|
|
|
|
// Analysis is terminated
|
|
|
|
return mExitCode;
|
2012-04-16 16:25:04 +02:00
|
|
|
} catch (const std::runtime_error &e) {
|
2023-08-31 13:33:29 +02:00
|
|
|
internalError(filename, std::string("Checking file failed: ") + e.what());
|
|
|
|
} catch (const std::bad_alloc &) {
|
|
|
|
internalError(filename, "Checking file failed: out of memory");
|
2013-05-09 18:50:24 +02:00
|
|
|
} catch (const InternalError &e) {
|
2023-09-20 10:40:57 +02:00
|
|
|
const ErrorMessage errmsg = ErrorMessage::fromInternalError(e, nullptr, filename, "Bailing out from analysis: Checking file failed");
|
|
|
|
reportErr(errmsg);
|
2011-04-24 18:10:25 +02:00
|
|
|
}
|
|
|
|
|
2022-09-27 20:04:35 +02:00
|
|
|
if (!mSettings.buildDir.empty()) {
|
|
|
|
mAnalyzerInformation.setFileInfo("CheckUnusedFunctions", checkUnusedFunctions.analyzerInfo());
|
|
|
|
mAnalyzerInformation.close();
|
|
|
|
}
|
2016-10-29 12:18:11 +02:00
|
|
|
|
2015-01-07 19:26:16 +01:00
|
|
|
// In jointSuppressionReport mode, unmatched suppressions are
|
|
|
|
// collected after all files are processed
|
2023-04-08 18:06:38 +02:00
|
|
|
if (!mSettings.useSingleJob() && (mSettings.severity.isEnabled(Severity::information) || mSettings.checkConfiguration)) {
|
2023-08-18 11:55:23 +02:00
|
|
|
Suppressions::reportUnmatchedSuppressions(mSettings.nomsg.getUnmatchedLocalSuppressions(filename, isUnusedFunctionCheckEnabled()), *this);
|
2015-01-07 19:26:16 +01:00
|
|
|
}
|
2010-04-13 19:25:08 +02:00
|
|
|
|
2018-06-16 23:31:16 +02:00
|
|
|
mErrorList.clear();
|
2018-09-08 11:09:49 +02:00
|
|
|
|
2023-10-05 19:04:06 +02:00
|
|
|
if (mSettings.showtime == SHOWTIME_MODES::SHOWTIME_FILE || mSettings.showtime == SHOWTIME_MODES::SHOWTIME_TOP5_FILE)
|
|
|
|
printTimerResults(mSettings.showtime);
|
|
|
|
|
2018-06-17 07:43:25 +02:00
|
|
|
return mExitCode;
|
2011-07-25 13:25:09 +02:00
|
|
|
}
|
|
|
|
|
2023-08-31 13:33:29 +02:00
|
|
|
// TODO: replace with ErrorMessage::fromInternalError()
|
2013-05-09 18:50:24 +02:00
|
|
|
void CppCheck::internalError(const std::string &filename, const std::string &msg)
|
|
|
|
{
|
2023-08-31 13:33:29 +02:00
|
|
|
const std::string fullmsg("Bailing out from analysis: " + msg);
|
2013-05-09 18:50:24 +02:00
|
|
|
|
2023-06-08 23:31:36 +02:00
|
|
|
const ErrorMessage::FileLocation loc1(filename, 0, 0);
|
|
|
|
std::list<ErrorMessage::FileLocation> callstack(1, loc1);
|
|
|
|
|
|
|
|
ErrorMessage errmsg(callstack,
|
|
|
|
emptyString,
|
|
|
|
Severity::error,
|
|
|
|
fullmsg,
|
|
|
|
"internalError",
|
|
|
|
Certainty::normal);
|
|
|
|
|
|
|
|
mErrorLogger.reportErr(errmsg);
|
2013-05-09 18:50:24 +02:00
|
|
|
}
|
2011-08-16 21:58:27 +02:00
|
|
|
|
2010-04-13 19:25:08 +02:00
|
|
|
//---------------------------------------------------------------------------
|
2015-12-11 10:22:06 +01:00
|
|
|
// CppCheck - A function that checks a raw token list
|
2010-04-13 19:25:08 +02:00
|
|
|
//---------------------------------------------------------------------------
|
2015-12-11 10:22:06 +01:00
|
|
|
void CppCheck::checkRawTokens(const Tokenizer &tokenizer)
|
2010-04-13 19:25:08 +02:00
|
|
|
{
|
2023-05-04 10:54:19 +02:00
|
|
|
#ifdef HAVE_RULES
|
2015-12-11 10:22:06 +01:00
|
|
|
// Execute rules for "raw" code
|
|
|
|
executeRules("raw", tokenizer);
|
2023-05-04 10:54:19 +02:00
|
|
|
#else
|
|
|
|
(void)tokenizer;
|
|
|
|
#endif
|
2015-12-11 10:22:06 +01:00
|
|
|
}
|
2010-04-13 19:25:08 +02:00
|
|
|
|
2015-12-11 10:22:06 +01:00
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
// CppCheck - A function that checks a normal token list
|
|
|
|
//---------------------------------------------------------------------------
|
2014-11-24 06:37:08 +01:00
|
|
|
|
2015-12-11 10:22:06 +01:00
|
|
|
void CppCheck::checkNormalTokens(const Tokenizer &tokenizer)
|
|
|
|
{
|
2022-10-06 20:54:24 +02:00
|
|
|
// 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);
|
|
|
|
|
2022-12-30 21:21:05 +01:00
|
|
|
const std::time_t maxTime = mSettings.checksMaxTime > 0 ? std::time(nullptr) + mSettings.checksMaxTime : 0;
|
|
|
|
|
2022-04-11 07:30:55 +02:00
|
|
|
// 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
|
|
|
|
2022-12-30 21:21:05 +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);
|
|
|
|
}
|
2022-04-11 07:30:55 +02:00
|
|
|
return;
|
2022-12-30 21:21:05 +01:00
|
|
|
}
|
2013-05-11 11:35:04 +02:00
|
|
|
|
2022-10-06 20:54:24 +02:00
|
|
|
if (doUnusedFunctionOnly && dynamic_cast<CheckUnusedFunctions*>(check) == nullptr)
|
|
|
|
continue;
|
|
|
|
|
2022-04-11 07:30:55 +02:00
|
|
|
Timer timerRunChecks(check->name() + "::runChecks", mSettings.showtime, &s_timerResults);
|
2023-08-18 12:03:50 +02:00
|
|
|
check->runChecks(tokenizer, this);
|
2022-04-11 07:30:55 +02:00
|
|
|
}
|
2019-09-17 21:00:59 +02:00
|
|
|
|
2022-04-11 07:30:55 +02:00
|
|
|
if (mSettings.clang)
|
|
|
|
// TODO: Use CTU for Clang analysis
|
|
|
|
return;
|
2021-04-30 16:47:02 +02:00
|
|
|
|
2018-12-25 21:11:23 +01:00
|
|
|
|
2023-04-08 18:06:38 +02:00
|
|
|
if (mSettings.useSingleJob() || !mSettings.buildDir.empty()) {
|
2022-09-27 20:04:35 +02:00
|
|
|
// Analyse the tokens..
|
|
|
|
|
|
|
|
CTU::FileInfo *fi1 = CTU::getFileInfo(&tokenizer);
|
|
|
|
if (fi1) {
|
2023-04-08 18:06:38 +02:00
|
|
|
if (mSettings.useSingleJob())
|
2022-09-27 20:04:35 +02:00
|
|
|
mFileInfo.push_back(fi1);
|
|
|
|
if (!mSettings.buildDir.empty())
|
|
|
|
mAnalyzerInformation.setFileInfo("ctu", fi1->toString());
|
|
|
|
}
|
2018-12-25 21:11:23 +01:00
|
|
|
|
2022-10-06 20:54:24 +02:00
|
|
|
for (const Check *check : Check::instances()) {
|
|
|
|
if (doUnusedFunctionOnly && dynamic_cast<const CheckUnusedFunctions*>(check) == nullptr)
|
|
|
|
continue;
|
|
|
|
|
2022-09-27 20:04:35 +02:00
|
|
|
Check::FileInfo *fi = check->getFileInfo(&tokenizer, &mSettings);
|
|
|
|
if (fi != nullptr) {
|
2023-04-08 18:06:38 +02:00
|
|
|
if (mSettings.useSingleJob())
|
2022-09-27 20:04:35 +02:00
|
|
|
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
|
|
|
}
|
2022-04-11 07:30:55 +02:00
|
|
|
|
2023-05-04 10:54:19 +02:00
|
|
|
#ifdef HAVE_RULES
|
2022-04-11 07:30:55 +02:00
|
|
|
executeRules("normal", tokenizer);
|
2023-05-04 10:54:19 +02:00
|
|
|
#endif
|
2015-12-11 10:22:06 +01:00
|
|
|
}
|
2010-04-13 19:25:08 +02:00
|
|
|
|
2015-12-11 10:22:06 +01:00
|
|
|
//---------------------------------------------------------------------------
|
2010-12-12 11:56:22 +01:00
|
|
|
|
2023-05-04 10:54:19 +02:00
|
|
|
#ifdef HAVE_RULES
|
2019-03-17 08:19:56 +01:00
|
|
|
bool CppCheck::hasRule(const std::string &tokenlist) const
|
2015-12-11 10:22:06 +01:00
|
|
|
{
|
2023-05-04 10:54:19 +02:00
|
|
|
return std::any_of(mSettings.rules.cbegin(), mSettings.rules.cend(), [&](const Settings::Rule& rule) {
|
|
|
|
return rule.tokenlist == tokenlist;
|
|
|
|
});
|
2010-04-13 19:25:08 +02:00
|
|
|
}
|
|
|
|
|
2018-09-23 14:27:46 +02:00
|
|
|
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:
|
2018-09-23 14:27:46 +02:00
|
|
|
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
|
2018-09-23 14:27:46 +02:00
|
|
|
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)";
|
2018-09-23 14:27:46 +02:00
|
|
|
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
|
2018-09-23 14:27:46 +02:00
|
|
|
}
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
2013-06-09 14:58:56 +02:00
|
|
|
void CppCheck::executeRules(const std::string &tokenlist, const Tokenizer &tokenizer)
|
|
|
|
{
|
|
|
|
// There is no rule to execute
|
2019-03-17 08:19:56 +01:00
|
|
|
if (!hasRule(tokenlist))
|
2013-06-09 14:58:56 +02:00
|
|
|
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) {
|
2015-11-28 12:30:03 +01:00
|
|
|
if (rule.pattern.empty() || rule.id.empty() || rule.severity == Severity::none || rule.tokenlist != tokenlist)
|
2013-06-09 14:58:56 +02:00
|
|
|
continue;
|
|
|
|
|
2020-11-22 08:46:50 +01:00
|
|
|
if (!mSettings.quiet) {
|
2021-07-08 21:21:35 +02:00
|
|
|
reportOut("Processing rule: " + rule.pattern, Color::FgGreen);
|
2020-11-22 08:46:50 +01:00
|
|
|
}
|
|
|
|
|
2018-09-23 14:27:46 +02:00
|
|
|
const char *pcreCompileErrorStr = nullptr;
|
2013-06-09 14:58:56 +02:00
|
|
|
int erroffset = 0;
|
2018-09-23 14:27:46 +02:00
|
|
|
pcre * const re = pcre_compile(rule.pattern.c_str(),0,&pcreCompileErrorStr,&erroffset,nullptr);
|
2013-06-09 14:58:56 +02:00
|
|
|
if (!re) {
|
2018-09-23 14:27:46 +02:00
|
|
|
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",
|
2021-02-24 22:00:06 +01:00
|
|
|
Certainty::normal);
|
2013-06-09 14:58:56 +02:00
|
|
|
|
|
|
|
reportErr(errmsg);
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-09-23 14:27:46 +02:00
|
|
|
// 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",
|
2021-02-24 22:00:06 +01:00
|
|
|
Certainty::normal);
|
2018-09-23 14:27:46 +02:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2013-06-09 14:58:56 +02:00
|
|
|
int pos = 0;
|
2015-05-25 21:15:55 +02:00
|
|
|
int ovector[30]= {0};
|
2018-09-23 14:27:46 +02:00
|
|
|
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) {
|
2018-09-24 09:00:09 +02:00
|
|
|
const std::string errorMessage = pcreErrorCodeToString(pcreExecRet);
|
2018-09-23 14:27:46 +02:00
|
|
|
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",
|
2021-02-24 22:00:06 +01:00
|
|
|
Certainty::normal);
|
2018-09-23 14:27:46 +02:00
|
|
|
|
|
|
|
reportErr(errmsg);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2015-05-25 21:15:55 +02:00
|
|
|
const unsigned int pos1 = (unsigned int)ovector[0];
|
|
|
|
const unsigned int pos2 = (unsigned int)ovector[1];
|
2013-06-09 14:58:56 +02:00
|
|
|
|
|
|
|
// 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;
|
2014-06-04 19:18:27 +02:00
|
|
|
loc.setfile(tokenizer.list.getSourceFilePath());
|
2013-06-09 14:58:56 +02:00
|
|
|
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);
|
2013-06-09 14:58:56 +02:00
|
|
|
|
|
|
|
// Create error message
|
|
|
|
std::string summary;
|
|
|
|
if (rule.summary.empty())
|
|
|
|
summary = "found '" + str.substr(pos1, pos2 - pos1) + "'";
|
|
|
|
else
|
|
|
|
summary = rule.summary;
|
2021-02-24 22:00:06 +01:00
|
|
|
const ErrorMessage errmsg(callStack, tokenizer.list.getSourceFilePath(), rule.severity, summary, rule.id, Certainty::normal);
|
2013-06-09 14:58:56 +02:00
|
|
|
|
|
|
|
// Report error
|
|
|
|
reportErr(errmsg);
|
|
|
|
}
|
|
|
|
|
|
|
|
pcre_free(re);
|
2018-09-26 08:07:26 +02:00
|
|
|
#ifdef PCRE_CONFIG_JIT
|
2018-09-23 14:27:46 +02:00
|
|
|
// Free up the EXTRA PCRE value (may be NULL at this point)
|
|
|
|
if (pcreExtra) {
|
|
|
|
pcre_free_study(pcreExtra);
|
|
|
|
}
|
2018-09-26 08:07:26 +02:00
|
|
|
#endif
|
2013-06-09 14:58:56 +02:00
|
|
|
}
|
|
|
|
}
|
2023-05-04 10:54:19 +02:00
|
|
|
#endif
|
2013-06-09 14:58:56 +02:00
|
|
|
|
2021-02-06 19:06:05 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2021-02-06 19:06:05 +01:00
|
|
|
|
2021-07-07 10:58:13 +02:00
|
|
|
void CppCheck::executeAddons(const std::vector<std::string>& files)
|
|
|
|
{
|
|
|
|
if (mSettings.addons.empty() || files.empty())
|
|
|
|
return;
|
2021-02-06 19:06:05 +01:00
|
|
|
|
2023-08-24 22:47:20 +02:00
|
|
|
FilesDeleter filesDeleter;
|
|
|
|
|
2021-07-09 08:33:07 +02:00
|
|
|
std::string fileList;
|
|
|
|
|
2021-09-28 20:34:21 +02:00
|
|
|
if (files.size() >= 2 || endsWith(files[0], ".ctu-info")) {
|
2023-10-05 11:44:48 +02:00
|
|
|
fileList = Path::getPathFromFilename(files[0]) + FILELIST + std::to_string(getPid());
|
2023-08-24 22:47:20 +02:00
|
|
|
filesDeleter.addFile(fileList);
|
2021-07-09 08:33:07 +02:00
|
|
|
std::ofstream fout(fileList);
|
|
|
|
for (const std::string& f: files)
|
|
|
|
fout << f << std::endl;
|
|
|
|
}
|
|
|
|
|
2023-10-08 21:28:57 +02:00
|
|
|
// ensure all addons have already been resolved - TODO: remove when settings are const after creation
|
|
|
|
assert(mSettings.addonInfos.size() == mSettings.addons.size());
|
|
|
|
|
|
|
|
for (const AddonInfo &addonInfo : mSettings.addonInfos) {
|
2022-02-13 11:58:42 +01:00
|
|
|
if (addonInfo.name != "misra" && !addonInfo.ctu && endsWith(files.back(), ".ctu-info"))
|
2021-07-07 10:58:13 +02:00
|
|
|
continue;
|
2021-02-06 19:06:05 +01:00
|
|
|
|
2023-09-20 10:40:57 +02:00
|
|
|
const std::vector<picojson::value> results =
|
2022-08-22 21:11:28 +02:00
|
|
|
executeAddon(addonInfo, mSettings.addonPython, fileList.empty() ? files[0] : fileList, mSettings.premiumArgs, mExecuteCommand);
|
2021-02-06 19:06:05 +01:00
|
|
|
|
2023-08-23 10:28:02 +02:00
|
|
|
const bool misraC2023 = mSettings.premiumArgs.find("--misra-c-2023") != std::string::npos;
|
|
|
|
|
2023-09-20 10:40:57 +02:00
|
|
|
for (const picojson::value& res : results) {
|
|
|
|
// TODO: get rid of copy?
|
|
|
|
// this is a copy so we can access missing fields and get a default value
|
2021-07-07 10:58:13 +02:00
|
|
|
picojson::object obj = res.get<picojson::object>();
|
2021-02-06 19:06:05 +01:00
|
|
|
|
2021-07-07 10:58:13 +02:00
|
|
|
ErrorMessage errmsg;
|
2021-02-06 19:06:05 +01:00
|
|
|
|
2021-10-11 22:07:03 +02:00
|
|
|
if (obj.count("file") > 0) {
|
2022-09-10 11:25:15 +02:00
|
|
|
std::string fileName = obj["file"].get<std::string>();
|
2021-10-11 22:07:03 +02:00
|
|
|
const int64_t lineNumber = obj["linenr"].get<int64_t>();
|
|
|
|
const int64_t column = obj["column"].get<int64_t>();
|
2022-09-10 11:25:15 +02:00
|
|
|
errmsg.callStack.emplace_back(std::move(fileName), lineNumber, column);
|
2021-10-11 22:07:03 +02:00
|
|
|
} else if (obj.count("loc") > 0) {
|
|
|
|
for (const picojson::value &locvalue: obj["loc"].get<picojson::array>()) {
|
|
|
|
picojson::object loc = locvalue.get<picojson::object>();
|
2022-09-10 11:25:15 +02:00
|
|
|
std::string fileName = loc["file"].get<std::string>();
|
2021-10-11 22:07:03 +02:00
|
|
|
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>();
|
2022-09-10 11:25:15 +02:00
|
|
|
errmsg.callStack.emplace_back(std::move(fileName), info, lineNumber, column);
|
2021-10-11 22:07:03 +02:00
|
|
|
}
|
|
|
|
}
|
2021-07-07 10:58:13 +02:00
|
|
|
|
|
|
|
errmsg.id = obj["addon"].get<std::string>() + "-" + obj["errorId"].get<std::string>();
|
2023-09-08 19:30:25 +02:00
|
|
|
if (misraC2023 && startsWith(errmsg.id, "misra-c2012-"))
|
2023-08-23 10:28:02 +02:00
|
|
|
errmsg.id = "misra-c2023-" + errmsg.id.substr(12);
|
2021-07-07 10:58:13 +02:00
|
|
|
const std::string text = obj["message"].get<std::string>();
|
|
|
|
errmsg.setmsg(text);
|
|
|
|
const std::string severity = obj["severity"].get<std::string>();
|
2023-10-12 11:58:39 +02:00
|
|
|
errmsg.severity = severityFromString(severity);
|
|
|
|
if (errmsg.severity == Severity::none) {
|
2023-08-31 18:28:47 +02:00
|
|
|
if (!endsWith(errmsg.id, "-logChecker"))
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if (!mSettings.severity.isEnabled(errmsg.severity))
|
2023-05-27 10:24:00 +02:00
|
|
|
continue;
|
2021-10-11 22:07:03 +02:00
|
|
|
errmsg.file0 = ((files.size() == 1) ? files[0] : "");
|
2021-07-07 10:58:13 +02:00
|
|
|
|
|
|
|
reportErr(errmsg);
|
2021-02-06 19:06:05 +01:00
|
|
|
}
|
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;
|
2021-07-22 18:53:44 +02:00
|
|
|
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));
|
2021-02-06 19:06:05 +01:00
|
|
|
}
|
|
|
|
|
2022-07-07 19:14:18 +02:00
|
|
|
try {
|
|
|
|
executeAddons(ctuInfoFiles);
|
2022-07-07 19:45:47 +02:00
|
|
|
} catch (const InternalError& e) {
|
2023-09-20 10:40:57 +02:00
|
|
|
const ErrorMessage errmsg = ErrorMessage::fromInternalError(e, nullptr, "", "Bailing out from analysis: Whole program analysis failed");
|
|
|
|
reportErr(errmsg);
|
2022-07-07 19:14:18 +02:00
|
|
|
}
|
2021-10-15 20:43:30 +02:00
|
|
|
|
2022-07-12 10:14:14 +02:00
|
|
|
if (mSettings.buildDir.empty()) {
|
|
|
|
for (const std::string &f: ctuInfoFiles)
|
|
|
|
std::remove(f.c_str());
|
2021-10-15 20:43:30 +02:00
|
|
|
}
|
2021-02-06 19:06:05 +01:00
|
|
|
}
|
|
|
|
|
2011-02-16 02:12:15 +01:00
|
|
|
Settings &CppCheck::settings()
|
2010-04-13 19:25:08 +02:00
|
|
|
{
|
2018-06-16 16:10:28 +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)
|
2012-12-26 18:35:49 +01:00
|
|
|
{
|
2021-02-24 22:00:06 +01:00
|
|
|
if (!mSettings.severity.isEnabled(Severity::information) && !mTooManyConfigs)
|
2012-12-26 18:35:49 +01:00
|
|
|
return;
|
|
|
|
|
2018-06-17 19:20:07 +02:00
|
|
|
mTooManyConfigs = false;
|
2012-12-26 18:35:49 +01:00
|
|
|
|
2021-02-24 22:00:06 +01:00
|
|
|
if (mSettings.severity.isEnabled(Severity::information) && file.empty())
|
2012-12-26 18:35:49 +01:00
|
|
|
return;
|
|
|
|
|
2020-05-23 07:16:49 +02:00
|
|
|
std::list<ErrorMessage::FileLocation> loclist;
|
2012-12-26 18:35:49 +01:00
|
|
|
if (!file.empty()) {
|
2022-09-29 21:47:17 +02:00
|
|
|
loclist.emplace_back(file);
|
2012-12-26 18:35:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
std::ostringstream msg;
|
2018-06-16 16:10:28 +02:00
|
|
|
msg << "Too many #ifdef configurations - cppcheck only checks " << mSettings.maxConfigs;
|
|
|
|
if (numberOfConfigurations > mSettings.maxConfigs)
|
2012-12-26 18:35:49 +01:00
|
|
|
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,
|
2021-02-24 22:00:06 +01:00
|
|
|
Certainty::normal);
|
2012-12-26 18:35:49 +01:00
|
|
|
|
|
|
|
reportErr(errmsg);
|
|
|
|
}
|
|
|
|
|
2014-09-02 18:05:02 +02:00
|
|
|
void CppCheck::purgedConfigurationMessage(const std::string &file, const std::string& configuration)
|
|
|
|
{
|
2018-06-17 19:20:07 +02:00
|
|
|
mTooManyConfigs = false;
|
2014-09-02 18:05:02 +02:00
|
|
|
|
2021-02-24 22:00:06 +01:00
|
|
|
if (mSettings.severity.isEnabled(Severity::information) && file.empty())
|
2014-09-02 18:05:02 +02:00
|
|
|
return;
|
|
|
|
|
2020-05-23 07:16:49 +02:00
|
|
|
std::list<ErrorMessage::FileLocation> loclist;
|
2014-09-02 18:05:02 +02:00
|
|
|
if (!file.empty()) {
|
2022-09-29 21:47:17 +02:00
|
|
|
loclist.emplace_back(file);
|
2014-09-02 18:05:02 +02:00
|
|
|
}
|
|
|
|
|
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",
|
2021-02-24 22:00:06 +01:00
|
|
|
Certainty::normal);
|
2014-09-02 18:05:02 +02:00
|
|
|
|
|
|
|
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
|
|
|
{
|
2023-08-31 18:28:47 +02:00
|
|
|
if (msg.severity == Severity::none && (msg.id == "logChecker" || endsWith(msg.id, "-logChecker"))) {
|
2023-08-29 12:00:52 +02:00
|
|
|
mErrorLogger.reportErr(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-06-16 16:10:28 +02:00
|
|
|
if (!mSettings.library.reportErrors(msg.file0))
|
2013-10-20 14:09:10 +02:00
|
|
|
return;
|
|
|
|
|
2018-06-16 16:10:28 +02:00
|
|
|
const std::string errmsg = msg.toString(mSettings.verbose);
|
2011-04-10 21:51:27 +02:00
|
|
|
if (errmsg.empty())
|
|
|
|
return;
|
2010-04-13 19:25:08 +02:00
|
|
|
|
2012-04-06 18:16:59 +02:00
|
|
|
// Alert only about unique errors
|
2022-12-30 15:13:47 +01:00
|
|
|
if (std::find(mErrorList.cbegin(), mErrorList.cend(), errmsg) != mErrorList.cend())
|
2012-04-06 18:16:59 +02:00
|
|
|
return;
|
|
|
|
|
2022-09-27 20:04:35 +02:00
|
|
|
if (!mSettings.buildDir.empty())
|
2022-12-11 21:27:16 +01:00
|
|
|
mAnalyzerInformation.reportErr(msg);
|
2021-06-12 18:19:00 +02:00
|
|
|
|
2023-10-16 19:43:15 +02:00
|
|
|
std::set<std::string> macroNames;
|
|
|
|
if (!msg.callStack.empty()) {
|
|
|
|
const std::string &file = msg.callStack.back().getfile(false);
|
|
|
|
int lineNumber = msg.callStack.back().line;
|
|
|
|
const auto it = mLocationMacros.find(Location(file, lineNumber));
|
|
|
|
if (it != mLocationMacros.cend())
|
|
|
|
macroNames = it->second;
|
|
|
|
}
|
|
|
|
|
2023-03-03 18:37:09 +01:00
|
|
|
// TODO: only convert if necessary
|
2023-10-16 19:43:15 +02:00
|
|
|
const auto errorMessage = Suppressions::ErrorMessage::fromErrorMessage(msg, macroNames);
|
2010-04-13 19:25:08 +02:00
|
|
|
|
2023-05-04 18:15:18 +02:00
|
|
|
if (mSettings.nomsg.isSuppressed(errorMessage, mUseGlobalSuppressions)) {
|
|
|
|
return;
|
2018-05-11 09:01:08 +02:00
|
|
|
}
|
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);
|
2012-04-06 18:16:59 +02:00
|
|
|
|
2018-06-16 16:10:28 +02:00
|
|
|
mErrorLogger.reportErr(msg);
|
2022-04-26 17:39:39 +02:00
|
|
|
// check if plistOutput should be populated and the current output file is open and the error is not suppressed
|
2022-09-24 11:59:13 +02:00
|
|
|
if (!mSettings.plistOutput.empty() && mPlistFile.is_open() && !mSettings.nomsg.isSuppressed(errorMessage)) {
|
2022-04-26 17:39:39 +02:00
|
|
|
// add error to plist output file
|
2022-09-24 11:59:13 +02:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2012-07-08 23:39:46 +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
|
|
|
{
|
2018-06-16 16:10:28 +02:00
|
|
|
mErrorLogger.reportProgress(filename, stage, value);
|
2010-04-13 19:25:08 +02:00
|
|
|
}
|
|
|
|
|
2023-04-16 13:54:21 +02:00
|
|
|
void CppCheck::getErrorMessages(ErrorLogger &errorlogger)
|
2010-04-13 19:25:08 +02:00
|
|
|
{
|
2023-04-16 13:54:21 +02:00
|
|
|
Settings s;
|
2021-02-24 22:00:06 +01:00
|
|
|
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);
|
2015-01-06 15:08:25 +01:00
|
|
|
|
2023-04-16 13:54:21 +02:00
|
|
|
CppCheck cppcheck(errorlogger, true, nullptr);
|
|
|
|
cppcheck.purgedConfigurationMessage(emptyString,emptyString);
|
|
|
|
cppcheck.mTooManyConfigs = true;
|
|
|
|
cppcheck.tooManyConfigsError(emptyString,0U);
|
|
|
|
// TODO: add functions to get remaining error messages
|
2012-12-26 18:35:49 +01:00
|
|
|
|
2010-04-13 19:25:08 +02:00
|
|
|
// call all "getErrorMessages" in all registered Check classes
|
2022-12-20 20:32:16 +01:00
|
|
|
for (std::list<Check *>::const_iterator it = Check::instances().cbegin(); it != Check::instances().cend(); ++it)
|
2023-04-16 13:54:21 +02:00
|
|
|
(*it)->getErrorMessages(&errorlogger, &s);
|
2010-04-13 19:25:08 +02:00
|
|
|
|
2023-04-16 13:54:21 +02:00
|
|
|
Preprocessor::getErrorMessages(&errorlogger, &s);
|
2010-04-13 19:25:08 +02:00
|
|
|
}
|
2014-11-15 10:43:49 +01:00
|
|
|
|
2023-11-02 17:42:41 +01:00
|
|
|
void CppCheck::analyseClangTidy(const FileSettings &fileSettings)
|
2020-01-30 07:14:17 +01:00
|
|
|
{
|
2021-01-02 19:10:25 +01:00
|
|
|
std::string allIncludes;
|
2020-01-30 07:14:17 +01:00
|
|
|
for (const std::string &inc : fileSettings.includePaths) {
|
|
|
|
allIncludes = allIncludes + "-I\"" + inc + "\" ";
|
|
|
|
}
|
|
|
|
|
2020-05-20 14:56:55 +02:00
|
|
|
const std::string allDefines = getDefinesFlags(fileSettings.defines);
|
2020-01-30 07:14:17 +01:00
|
|
|
|
2020-05-19 16:04:25 +02:00
|
|
|
#ifdef _WIN32
|
2023-11-03 09:55:44 +01:00
|
|
|
constexpr char exe[] = "clang-tidy.exe";
|
2020-05-19 16:04:25 +02:00
|
|
|
#else
|
2023-11-03 09:55:44 +01:00
|
|
|
constexpr char exe[] = "clang-tidy";
|
2020-05-19 16:04:25 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
const std::string args = "-quiet -checks=*,-clang-analyzer-*,-llvm* \"" + fileSettings.filename + "\" -- " + allIncludes + allDefines;
|
|
|
|
std::string output;
|
2023-09-20 10:40:57 +02:00
|
|
|
if (const int exitcode = mExecuteCommand(exe, split(args), emptyString, output)) {
|
|
|
|
std::cerr << "Failed to execute '" << exe << "' (exitcode: " << std::to_string(exitcode) << ")" << 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
|
2020-05-19 16:04:25 +02:00
|
|
|
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()) {
|
2022-07-10 10:57:29 +02:00
|
|
|
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:");
|
|
|
|
}
|
|
|
|
|
2023-02-08 21:01:51 +01:00
|
|
|
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));
|
2023-04-08 22:29:09 +02:00
|
|
|
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;
|
2022-07-26 15:30:53 +02:00
|
|
|
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)
|
2023-10-12 11:58:39 +02:00
|
|
|
errmsg.severity = Severity::performance;
|
2020-01-30 07:14:17 +01:00
|
|
|
else if (errmsg.id.find("portability") != std::string::npos)
|
2023-10-12 11:58:39 +02:00
|
|
|
errmsg.severity = Severity::portability;
|
2020-01-30 07:14:17 +01:00
|
|
|
else if (errmsg.id.find("cert") != std::string::npos || errmsg.id.find("misc") != std::string::npos || errmsg.id.find("unused") != std::string::npos)
|
2023-10-12 11:58:39 +02:00
|
|
|
errmsg.severity = Severity::warning;
|
2020-01-30 07:14:17 +01:00
|
|
|
else
|
2023-10-12 11:58:39 +02:00
|
|
|
errmsg.severity = Severity::style;
|
2020-01-30 07:14:17 +01:00
|
|
|
|
|
|
|
errmsg.file0 = fixedpath;
|
|
|
|
errmsg.setmsg(messageString);
|
|
|
|
reportErr(errmsg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-12 08:24:01 +01:00
|
|
|
bool CppCheck::analyseWholeProgram()
|
2014-11-15 10:43:49 +01:00
|
|
|
{
|
2018-01-12 08:24:01 +01:00
|
|
|
bool errors = false;
|
2019-02-03 17:51:02 +01:00
|
|
|
// Init CTU
|
|
|
|
CTU::maxCtuDepth = mSettings.maxCtuDepth;
|
2015-01-07 19:26:16 +01:00
|
|
|
// 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) {
|
2022-12-30 15:13:47 +01:00
|
|
|
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);
|
2014-11-15 10:43:49 +01:00
|
|
|
}
|
|
|
|
|
2023-11-03 23:24:04 +01:00
|
|
|
void CppCheck::analyseWholeProgram(const std::string &buildDir, const std::map<std::string, std::size_t> &files, const std::list<FileSettings>& fileSettings)
|
2016-11-05 21:26:56 +01:00
|
|
|
{
|
2023-11-03 23:24:04 +01:00
|
|
|
executeAddonsWholeProgram(files); // TODO: pass FileSettings
|
2023-01-18 17:32:14 +01:00
|
|
|
if (buildDir.empty()) {
|
2023-11-03 23:24:04 +01:00
|
|
|
removeCtuInfoFiles(files, fileSettings);
|
2016-11-05 21:26:56 +01:00
|
|
|
return;
|
2023-01-18 17:32:14 +01:00
|
|
|
}
|
2021-02-24 22:00:06 +01:00
|
|
|
if (mSettings.checks.isEnabled(Checks::unusedFunction))
|
2023-01-18 20:52:33 +01:00
|
|
|
CheckUnusedFunctions::analyseWholeProgram(mSettings, this, buildDir);
|
2017-03-30 10:14:17 +02:00
|
|
|
std::list<Check::FileInfo*> fileInfoList;
|
2018-12-25 21:11:23 +01:00
|
|
|
CTU::FileInfo ctuFileInfo;
|
2017-03-30 10:14:17 +02:00
|
|
|
|
|
|
|
// Load all analyzer info data..
|
|
|
|
const std::string filesTxt(buildDir + "/files.txt");
|
2018-04-11 09:50:42 +02:00
|
|
|
std::ifstream fin(filesTxt);
|
2017-03-30 10:14:17 +02:00
|
|
|
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);
|
2017-03-30 10:14:17 +02:00
|
|
|
|
|
|
|
tinyxml2::XMLDocument doc;
|
2018-05-29 13:24:48 +02:00
|
|
|
const tinyxml2::XMLError error = doc.LoadFile(xmlfile.c_str());
|
2017-03-30 10:14:17 +02:00
|
|
|
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;
|
|
|
|
}
|
2023-05-04 11:10:58 +02:00
|
|
|
for (const Check *check : Check::instances()) {
|
2018-12-25 21:11:23 +01:00
|
|
|
if (checkClassAttr == check->name())
|
|
|
|
fileInfoList.push_back(check->loadFileInfoFromXml(e));
|
2017-03-30 10:14:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-03 17:51:02 +01:00
|
|
|
// Set CTU max depth
|
|
|
|
CTU::maxCtuDepth = mSettings.maxCtuDepth;
|
|
|
|
|
2017-03-30 10:14:17 +02:00
|
|
|
// Analyse the tokens
|
2018-12-25 21:11:23 +01:00
|
|
|
for (Check *check : Check::instances())
|
|
|
|
check->analyseWholeProgram(&ctuFileInfo, fileInfoList, mSettings, *this);
|
2017-03-30 10:14:17 +02:00
|
|
|
|
2018-12-25 21:11:23 +01:00
|
|
|
for (Check::FileInfo *fi : fileInfoList)
|
|
|
|
delete fi;
|
2016-11-05 21:26:56 +01:00
|
|
|
}
|
|
|
|
|
2016-10-28 12:10:19 +02:00
|
|
|
bool CppCheck::isUnusedFunctionCheckEnabled() const
|
2015-01-07 19:26:16 +01:00
|
|
|
{
|
2023-04-08 18:06:38 +02:00
|
|
|
return (mSettings.useSingleJob() && mSettings.checks.isEnabled(Checks::unusedFunction));
|
2015-01-07 19:26:16 +01:00
|
|
|
}
|
2023-01-18 17:32:14 +01:00
|
|
|
|
2023-11-03 23:24:04 +01:00
|
|
|
void CppCheck::removeCtuInfoFiles(const std::map<std::string, std::size_t> &files, const std::list<FileSettings>& fileSettings)
|
2023-01-18 17:32:14 +01:00
|
|
|
{
|
|
|
|
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());
|
|
|
|
}
|
2023-11-03 23:24:04 +01:00
|
|
|
for (const auto& fs: fileSettings) {
|
2023-04-06 20:17:19 +02:00
|
|
|
const std::string &dumpFileName = getDumpFileName(mSettings, fs.filename);
|
|
|
|
const std::string &ctuInfoFileName = getCtuInfoFileName(dumpFileName);
|
|
|
|
std::remove(ctuInfoFileName.c_str());
|
|
|
|
}
|
2023-01-18 17:32:14 +01:00
|
|
|
}
|
|
|
|
}
|
2023-10-05 19:04:06 +02:00
|
|
|
|
|
|
|
// cppcheck-suppress unusedFunction - only used in tests
|
|
|
|
void CppCheck::resetTimerResults()
|
|
|
|
{
|
|
|
|
s_timerResults.reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CppCheck::printTimerResults(SHOWTIME_MODES mode)
|
|
|
|
{
|
|
|
|
s_timerResults.showResults(mode);
|
|
|
|
}
|