2010-04-13 19:25:08 +02:00
|
|
|
/*
|
|
|
|
* Cppcheck - A tool for static C/C++ code analysis
|
2020-05-10 11:16:32 +02:00
|
|
|
* Copyright (C) 2007-2020 Cppcheck team.
|
2010-04-13 19:25:08 +02:00
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
#include "cppcheck.h"
|
|
|
|
|
|
|
|
#include "check.h"
|
2016-10-29 12:18:11 +02:00
|
|
|
#include "checkunusedfunctions.h"
|
2020-01-10 16:29:06 +01:00
|
|
|
#include "clangimport.h"
|
2018-12-25 21:11:23 +01:00
|
|
|
#include "ctu.h"
|
2017-05-27 04:33:47 +02:00
|
|
|
#include "library.h"
|
|
|
|
#include "mathlib.h"
|
|
|
|
#include "path.h"
|
|
|
|
#include "platform.h"
|
|
|
|
#include "preprocessor.h" // Preprocessor
|
|
|
|
#include "suppressions.h"
|
2017-03-30 10:14:17 +02:00
|
|
|
#include "timer.h"
|
2017-05-27 04:33:47 +02:00
|
|
|
#include "tokenize.h" // Tokenizer
|
|
|
|
#include "tokenlist.h"
|
2017-03-30 10:14:17 +02:00
|
|
|
#include "version.h"
|
2016-10-29 12:18:11 +02:00
|
|
|
|
2019-09-17 21:00:59 +02:00
|
|
|
#include "exprengine.h"
|
|
|
|
|
2019-06-24 19:28:52 +02:00
|
|
|
#define PICOJSON_USE_INT64
|
2019-04-19 14:52:30 +02:00
|
|
|
#include <picojson.h>
|
2017-05-18 22:25:49 +02:00
|
|
|
#include <simplecpp.h>
|
2017-05-27 04:33:47 +02:00
|
|
|
#include <tinyxml2.h>
|
2010-04-13 19:25:08 +02:00
|
|
|
#include <algorithm>
|
2017-05-27 04:33:47 +02:00
|
|
|
#include <cstring>
|
|
|
|
#include <new>
|
|
|
|
#include <set>
|
2010-09-06 21:00:56 +02:00
|
|
|
#include <stdexcept>
|
2017-05-27 04:33:47 +02:00
|
|
|
#include <vector>
|
2019-04-07 17:01:59 +02:00
|
|
|
#include <memory>
|
|
|
|
#include <iostream> // <- TEMPORARY
|
2019-12-17 08:40:59 +01:00
|
|
|
#include <cstdio>
|
2010-04-13 19:25:08 +02:00
|
|
|
|
2011-02-14 19:37:58 +01:00
|
|
|
#ifdef HAVE_RULES
|
2010-12-12 11:56:22 +01:00
|
|
|
#define PCRE_STATIC
|
|
|
|
#include <pcre.h>
|
2010-12-31 10:24:51 +01:00
|
|
|
#endif
|
2010-12-12 11:56:22 +01:00
|
|
|
|
2014-01-04 10:41:13 +01:00
|
|
|
static const char Version[] = CPPCHECK_VERSION_STRING;
|
2011-08-11 16:34:59 +02:00
|
|
|
static const char ExtraVersion[] = "";
|
|
|
|
|
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
|
|
|
|
|
2019-04-08 19:42:16 +02:00
|
|
|
namespace {
|
|
|
|
struct AddonInfo {
|
2019-04-14 15:00:03 +02:00
|
|
|
std::string name;
|
|
|
|
std::string scriptFile;
|
2019-04-08 19:42:16 +02:00
|
|
|
std::string args;
|
2020-04-05 10:57:28 +02:00
|
|
|
std::string python;
|
2019-04-08 19:42:16 +02:00
|
|
|
|
2019-06-29 07:59:55 +02:00
|
|
|
static std::string getFullPath(const std::string &fileName, const std::string &exename) {
|
2019-06-16 15:46:01 +02:00
|
|
|
if (Path::fileExists(fileName))
|
|
|
|
return fileName;
|
|
|
|
|
|
|
|
const std::string exepath = Path::getPathFromFilename(exename);
|
|
|
|
if (Path::fileExists(exepath + fileName))
|
|
|
|
return exepath + fileName;
|
|
|
|
if (Path::fileExists(exepath + "addons/" + fileName))
|
|
|
|
return exepath + "addons/" + fileName;
|
|
|
|
|
2019-08-17 10:53:07 +02:00
|
|
|
#ifdef FILESDIR
|
|
|
|
if (Path::fileExists(FILESDIR + ("/" + fileName)))
|
|
|
|
return FILESDIR + ("/" + fileName);
|
|
|
|
if (Path::fileExists(FILESDIR + ("/addons/" + fileName)))
|
|
|
|
return FILESDIR + ("/addons/" + fileName);
|
2019-06-16 15:46:01 +02:00
|
|
|
#endif
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
2020-05-17 16:50:02 +02:00
|
|
|
std::string parseAddonInfo(const picojson::value &json, const std::string &fileName, const std::string &exename) {
|
2021-01-02 19:10:25 +01:00
|
|
|
const std::string& json_error = picojson::get_last_error();
|
2020-05-17 16:50:02 +02:00
|
|
|
if (!json_error.empty()) {
|
|
|
|
return "Loading " + fileName + " failed. " + json_error;
|
|
|
|
}
|
|
|
|
if (!json.is<picojson::object>())
|
|
|
|
return "Loading " + fileName + " failed. Bad json.";
|
|
|
|
picojson::object obj = json.get<picojson::object>();
|
|
|
|
if (obj.count("args")) {
|
|
|
|
if (!obj["args"].is<picojson::array>())
|
|
|
|
return "Loading " + fileName + " failed. args must be array.";
|
|
|
|
for (const picojson::value &v : obj["args"].get<picojson::array>())
|
|
|
|
args += " " + v.get<std::string>();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (obj.count("python")) {
|
|
|
|
// Python was defined in the config file
|
|
|
|
if (obj["python"].is<picojson::array>()) {
|
|
|
|
return "Loading " + fileName +" failed. python must not be an array.";
|
|
|
|
}
|
|
|
|
python = obj["python"].get<std::string>();
|
|
|
|
} else {
|
|
|
|
python = "";
|
|
|
|
}
|
|
|
|
|
|
|
|
return getAddonInfo(obj["script"].get<std::string>(), exename);
|
|
|
|
}
|
|
|
|
|
2019-04-14 15:00:03 +02:00
|
|
|
std::string getAddonInfo(const std::string &fileName, const std::string &exename) {
|
2020-05-17 16:50:02 +02:00
|
|
|
if (fileName[0] == '{') {
|
|
|
|
std::istringstream in(fileName);
|
|
|
|
picojson::value json;
|
|
|
|
in >> json;
|
|
|
|
return parseAddonInfo(json, fileName, exename);
|
|
|
|
}
|
2019-06-16 19:11:12 +02:00
|
|
|
if (fileName.find(".") == std::string::npos)
|
|
|
|
return getAddonInfo(fileName + ".py", exename);
|
|
|
|
|
2019-06-16 15:46:01 +02:00
|
|
|
if (endsWith(fileName, ".py", 3)) {
|
|
|
|
scriptFile = getFullPath(fileName, exename);
|
|
|
|
if (scriptFile.empty())
|
|
|
|
return "Did not find addon " + fileName;
|
|
|
|
|
|
|
|
std::string::size_type pos1 = scriptFile.rfind("/");
|
|
|
|
if (pos1 == std::string::npos)
|
|
|
|
pos1 = 0;
|
|
|
|
else
|
|
|
|
pos1++;
|
|
|
|
std::string::size_type pos2 = scriptFile.rfind(".");
|
|
|
|
if (pos2 < pos1)
|
|
|
|
pos2 = std::string::npos;
|
|
|
|
name = scriptFile.substr(pos1, pos2 - pos1);
|
|
|
|
|
2019-04-08 19:42:16 +02:00
|
|
|
return "";
|
|
|
|
}
|
2019-06-16 15:46:01 +02:00
|
|
|
|
|
|
|
if (!endsWith(fileName, ".json", 5))
|
|
|
|
return "Failed to open addon " + fileName;
|
|
|
|
|
2019-04-08 19:42:16 +02:00
|
|
|
std::ifstream fin(fileName);
|
|
|
|
if (!fin.is_open())
|
|
|
|
return "Failed to open " + fileName;
|
|
|
|
picojson::value json;
|
|
|
|
fin >> json;
|
2020-05-17 16:50:02 +02:00
|
|
|
return parseAddonInfo(json, fileName, exename);
|
2019-04-08 19:42:16 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
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);
|
2020-05-17 21:45:37 +02:00
|
|
|
if (f.find(" ") != std::string::npos)
|
|
|
|
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] == '\"') {
|
|
|
|
const std::string::size_type endPos = str.find("\"", startPos + 1);
|
|
|
|
ret.push_back(str.substr(startPos + 1, endPos - startPos - 1));
|
|
|
|
startPos = (endPos < str.size()) ? (endPos + 1) : endPos;
|
|
|
|
continue;
|
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
|
|
|
}
|
|
|
|
|
2021-02-06 19:06:05 +01:00
|
|
|
static void createDumpFile(const Settings& settings,
|
|
|
|
const std::string& filename,
|
|
|
|
const std::vector<std::string>& files,
|
|
|
|
const simplecpp::Token* rawtokens,
|
|
|
|
std::ofstream& fdump,
|
|
|
|
std::string& dumpFile)
|
|
|
|
{
|
|
|
|
if (!settings.dump && settings.addons.empty())
|
|
|
|
return;
|
|
|
|
if (!settings.dumpFile.empty())
|
|
|
|
dumpFile = settings.dumpFile;
|
|
|
|
else if (!settings.dump && !settings.buildDir.empty())
|
|
|
|
dumpFile = AnalyzerInformation::getAnalyzerInfoFile(settings.buildDir, filename, "") + ".dump";
|
|
|
|
else
|
|
|
|
dumpFile = filename + ".dump";
|
|
|
|
|
|
|
|
fdump.open(dumpFile);
|
|
|
|
if (!fdump.is_open())
|
|
|
|
return;
|
|
|
|
fdump << "<?xml version=\"1.0\"?>" << std::endl;
|
|
|
|
fdump << "<dumps>" << std::endl;
|
|
|
|
fdump << " <platform"
|
|
|
|
<< " name=\"" << settings.platformString() << '\"'
|
|
|
|
<< " char_bit=\"" << settings.char_bit << '\"'
|
|
|
|
<< " short_bit=\"" << settings.short_bit << '\"'
|
|
|
|
<< " int_bit=\"" << settings.int_bit << '\"'
|
|
|
|
<< " long_bit=\"" << settings.long_bit << '\"'
|
|
|
|
<< " long_long_bit=\"" << settings.long_long_bit << '\"'
|
|
|
|
<< " pointer_bit=\"" << (settings.sizeof_pointer * settings.char_bit) << '\"'
|
|
|
|
<< "/>\n";
|
|
|
|
if (rawtokens) {
|
|
|
|
fdump << " <rawtokens>" << std::endl;
|
|
|
|
for (unsigned int i = 0; i < files.size(); ++i)
|
|
|
|
fdump << " <file index=\"" << i << "\" name=\"" << ErrorLogger::toxml(files[i]) << "\"/>" << std::endl;
|
|
|
|
for (const simplecpp::Token *tok = rawtokens; tok; tok = tok->next) {
|
|
|
|
fdump << " <tok "
|
|
|
|
<< "fileIndex=\"" << tok->location.fileIndex << "\" "
|
|
|
|
<< "linenr=\"" << tok->location.line << "\" "
|
|
|
|
<< "column=\"" << tok->location.col << "\" "
|
|
|
|
<< "str=\"" << ErrorLogger::toxml(tok->str()) << "\""
|
|
|
|
<< "/>" << std::endl;
|
|
|
|
}
|
|
|
|
fdump << " </rawtokens>" << std::endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-05 10:57:28 +02:00
|
|
|
static std::string executeAddon(const AddonInfo &addonInfo,
|
|
|
|
const std::string &defaultPythonExe,
|
2020-05-19 16:04:25 +02:00
|
|
|
const std::string &dumpFile,
|
|
|
|
std::function<bool(std::string,std::vector<std::string>,std::string,std::string*)> 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
|
|
|
|
2020-05-19 08:14:56 +02:00
|
|
|
if (!addonInfo.python.empty())
|
|
|
|
pythonExe = cmdFileName(addonInfo.python);
|
|
|
|
else if (!defaultPythonExe.empty())
|
|
|
|
pythonExe = cmdFileName(defaultPythonExe);
|
|
|
|
else {
|
2019-12-17 12:00:01 +01:00
|
|
|
#ifdef _WIN32
|
2020-09-21 19:30:47 +02:00
|
|
|
const char *py_exes[] = { "python3.exe", "python.exe" };
|
2019-12-17 12:00:01 +01:00
|
|
|
#else
|
2020-09-21 19:30:47 +02:00
|
|
|
const char *py_exes[] = { "python3", "python" };
|
2019-12-17 12:00:01 +01:00
|
|
|
#endif
|
2020-09-21 19:30:47 +02:00
|
|
|
for (const char* py_exe : py_exes) {
|
2020-05-19 08:14:56 +02:00
|
|
|
std::string out;
|
2020-09-21 19:30:47 +02:00
|
|
|
if (executeCommand(py_exe, split("--version"), redirect, &out) && out.compare(0, 7, "Python ") == 0 && std::isdigit(out[7])) {
|
|
|
|
pythonExe = py_exe;
|
2020-05-19 08:14:56 +02:00
|
|
|
break;
|
|
|
|
}
|
2020-05-18 07:44:46 +02:00
|
|
|
}
|
2020-05-19 08:14:56 +02:00
|
|
|
if (pythonExe.empty())
|
|
|
|
throw InternalError(nullptr, "Failed to auto detect python");
|
2019-12-17 12:00:01 +01:00
|
|
|
}
|
|
|
|
|
2020-05-19 08:14:56 +02:00
|
|
|
const std::string args = cmdFileName(addonInfo.scriptFile) + " --cli" + addonInfo.args + " " + cmdFileName(dumpFile);
|
2019-04-14 15:00:03 +02:00
|
|
|
std::string result;
|
2020-05-19 16:04:25 +02:00
|
|
|
if (!executeCommand(pythonExe, split(args), redirect, &result))
|
2020-05-19 08:14:56 +02:00
|
|
|
throw InternalError(nullptr, "Failed to execute addon (command: '" + pythonExe + " " + args + "')");
|
2019-12-17 12:00:01 +01:00
|
|
|
|
|
|
|
// Validate output..
|
|
|
|
std::istringstream istr(result);
|
|
|
|
std::string line;
|
|
|
|
while (std::getline(istr, line)) {
|
|
|
|
if (line.compare(0,9,"Checking ", 0, 9) != 0 && !line.empty() && line[0] != '{')
|
2020-05-19 08:14:56 +02:00
|
|
|
throw InternalError(nullptr, "Failed to execute '" + pythonExe + " " + args + "'. " + result);
|
2019-12-17 12:00:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Valid results
|
2019-04-14 15:00:03 +02:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
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,
|
|
|
|
std::function<bool(std::string,std::vector<std::string>,std::string,std::string*)> executeCommand)
|
|
|
|
: mErrorLogger(errorLogger)
|
|
|
|
, mExitCode(0)
|
|
|
|
, mSuppressInternalErrorFound(false)
|
|
|
|
, mUseGlobalSuppressions(useGlobalSuppressions)
|
|
|
|
, mTooManyConfigs(false)
|
2020-05-30 11:23:22 +02:00
|
|
|
, mSimplify(true)
|
2020-05-19 16:04:25 +02:00
|
|
|
, mExecuteCommand(executeCommand)
|
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
|
|
|
}
|
2019-11-20 15:37:09 +01:00
|
|
|
s_timerResults.showResults(mSettings.showtime);
|
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
|
|
|
}
|
|
|
|
|
2020-05-23 07:16:49 +02:00
|
|
|
static bool reportClangErrors(std::istream &is, std::function<void(const ErrorMessage&)> reportErr)
|
2020-04-22 10:27:29 +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:");
|
|
|
|
if (pos3 == std::string::npos)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// file:line:column: error: ....
|
|
|
|
const std::string::size_type pos2 = line.rfind(":", pos3 - 1);
|
|
|
|
const std::string::size_type pos1 = line.rfind(":", pos2 - 1);
|
|
|
|
|
|
|
|
if (pos1 >= pos2 || pos2 >= pos3)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
const std::string filename = line.substr(0, pos1);
|
|
|
|
const std::string linenr = line.substr(pos1+1, pos2-pos1-1);
|
|
|
|
const std::string colnr = line.substr(pos2+1, pos3-pos2-1);
|
|
|
|
const std::string msg = line.substr(line.find(":", pos3+1) + 2);
|
|
|
|
|
2020-05-23 07:16:49 +02:00
|
|
|
std::list<ErrorMessage::FileLocation> locationList;
|
|
|
|
ErrorMessage::FileLocation loc;
|
2020-04-22 10:27:29 +02:00
|
|
|
loc.setfile(Path::toNativeSeparators(filename));
|
|
|
|
loc.line = std::atoi(linenr.c_str());
|
|
|
|
loc.column = std::atoi(colnr.c_str());
|
|
|
|
locationList.push_back(loc);
|
2020-05-23 07:16:49 +02:00
|
|
|
ErrorMessage errmsg(locationList,
|
|
|
|
loc.getfile(),
|
|
|
|
Severity::error,
|
|
|
|
msg,
|
|
|
|
"syntaxError",
|
2021-02-24 22:00:06 +01:00
|
|
|
Certainty::normal);
|
2020-04-22 10:27:29 +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
|
|
|
{
|
2020-01-05 15:12:53 +01:00
|
|
|
if (mSettings.clang) {
|
2020-02-02 16:35:42 +01:00
|
|
|
if (!mSettings.quiet)
|
|
|
|
mErrorLogger.reportOut(std::string("Checking ") + path + "...");
|
2020-01-11 12:16:48 +01:00
|
|
|
|
2020-04-21 22:26:34 +02:00
|
|
|
const std::string lang = Path::isCPP(path) ? "-x c++" : "-x c";
|
|
|
|
const std::string analyzerInfo = mSettings.buildDir.empty() ? std::string() : AnalyzerInformation::getAnalyzerInfoFile(mSettings.buildDir, path, "");
|
|
|
|
const std::string clangcmd = analyzerInfo + ".clang-cmd";
|
|
|
|
const std::string clangStderr = analyzerInfo + ".clang-stderr";
|
2020-12-31 17:37:02 +01:00
|
|
|
const std::string clangAst = analyzerInfo + ".clang-ast";
|
2020-09-06 07:47:17 +02:00
|
|
|
std::string exe = mSettings.clangExecutable;
|
2020-05-19 16:04:25 +02:00
|
|
|
#ifdef _WIN32
|
2020-09-06 07:47:17 +02:00
|
|
|
// append .exe if it is not a path
|
2020-09-06 07:46:51 +02:00
|
|
|
if (Path::fromNativeSeparators(mSettings.clangExecutable).find('/') == std::string::npos) {
|
|
|
|
exe += ".exe";
|
|
|
|
}
|
2020-05-19 16:04:25 +02:00
|
|
|
#endif
|
2020-05-24 10:52:43 +02:00
|
|
|
|
2020-04-21 22:26:34 +02:00
|
|
|
std::string flags(lang + " ");
|
2020-09-04 15:29:19 +02:00
|
|
|
if (Path::isCPP(path) && !mSettings.standards.stdValue.empty())
|
|
|
|
flags += "-std=" + mSettings.standards.stdValue + " ";
|
2020-01-08 16:36:51 +01:00
|
|
|
|
2020-01-09 19:15:01 +01:00
|
|
|
for (const std::string &i: mSettings.includePaths)
|
|
|
|
flags += "-I" + i + " ";
|
|
|
|
|
2020-05-20 14:56:55 +02:00
|
|
|
flags += getDefinesFlags(mSettings.userDefines);
|
|
|
|
|
2020-05-24 10:52:43 +02:00
|
|
|
const std::string args2 = "-fsyntax-only -Xclang -ast-dump -fno-color-diagnostics " + flags + path;
|
2020-05-19 16:04:25 +02:00
|
|
|
const std::string redirect2 = analyzerInfo.empty() ? std::string("2>&1") : ("2> " + clangStderr);
|
2020-04-21 22:26:34 +02:00
|
|
|
if (!mSettings.buildDir.empty()) {
|
|
|
|
std::ofstream fout(clangcmd);
|
2020-05-19 16:04:25 +02:00
|
|
|
fout << exe << " " << args2 << " " << redirect2 << std::endl;
|
2020-12-29 10:20:46 +01:00
|
|
|
} else if (mSettings.verbose && !mSettings.quiet) {
|
|
|
|
mErrorLogger.reportOut(exe + " " + args2);
|
2020-04-21 22:26:34 +02:00
|
|
|
}
|
2020-01-20 21:27:49 +01:00
|
|
|
|
2020-05-19 16:04:25 +02:00
|
|
|
std::string output2;
|
|
|
|
if (!mExecuteCommand(exe,split(args2),redirect2,&output2) || output2.find("TranslationUnitDecl") == std::string::npos) {
|
|
|
|
std::cerr << "Failed to execute '" << exe << " " << args2 << " " << redirect2 << "'" << std::endl;
|
2020-01-05 15:12:53 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2020-01-19 17:30:57 +01:00
|
|
|
|
|
|
|
// Ensure there are not syntax errors...
|
2020-04-21 22:26:34 +02:00
|
|
|
if (!mSettings.buildDir.empty()) {
|
2020-01-20 11:12:23 +01:00
|
|
|
std::ifstream fin(clangStderr);
|
2020-05-23 07:16:49 +02:00
|
|
|
auto reportError = [this](const ErrorMessage& errorMessage) {
|
2020-04-22 10:27:29 +02:00
|
|
|
reportErr(errorMessage);
|
|
|
|
};
|
|
|
|
if (reportClangErrors(fin, reportError))
|
|
|
|
return 0;
|
|
|
|
} else {
|
2020-05-19 16:04:25 +02:00
|
|
|
std::istringstream istr(output2);
|
2020-05-23 07:16:49 +02:00
|
|
|
auto reportError = [this](const ErrorMessage& errorMessage) {
|
2020-04-22 10:27:29 +02:00
|
|
|
reportErr(errorMessage);
|
|
|
|
};
|
|
|
|
if (reportClangErrors(istr, reportError))
|
2020-04-13 12:52:24 +02:00
|
|
|
return 0;
|
2020-01-19 17:30:57 +01:00
|
|
|
}
|
|
|
|
|
2020-12-31 17:37:02 +01:00
|
|
|
if (!mSettings.buildDir.empty()) {
|
|
|
|
std::ofstream fout(clangAst);
|
|
|
|
fout << output2 << std::endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
std::istringstream ast(output2);
|
|
|
|
Tokenizer tokenizer(&mSettings, this);
|
|
|
|
tokenizer.list.appendFileIfNew(path);
|
|
|
|
clangimport::parseClangAstDump(&tokenizer, ast);
|
|
|
|
ValueFlow::setValues(&tokenizer.list, const_cast<SymbolDatabase *>(tokenizer.getSymbolDatabase()), this, &mSettings);
|
|
|
|
if (mSettings.debugnormal)
|
|
|
|
tokenizer.printDebugOutput(1);
|
|
|
|
checkNormalTokens(tokenizer);
|
2021-02-06 19:06:05 +01:00
|
|
|
|
|
|
|
// create dumpfile
|
|
|
|
std::ofstream fdump;
|
|
|
|
std::string dumpFile;
|
|
|
|
createDumpFile(mSettings, path, tokenizer.list.getFiles(), nullptr, fdump, dumpFile);
|
|
|
|
if (fdump.is_open()) {
|
|
|
|
fdump << "<dump cfg=\"\">" << std::endl;
|
|
|
|
fdump << " <standards>" << std::endl;
|
|
|
|
fdump << " <c version=\"" << mSettings.standards.getC() << "\"/>" << std::endl;
|
|
|
|
fdump << " <cpp version=\"" << mSettings.standards.getCPP() << "\"/>" << std::endl;
|
|
|
|
fdump << " </standards>" << std::endl;
|
|
|
|
tokenizer.dump(fdump);
|
|
|
|
fdump << "</dump>" << std::endl;
|
|
|
|
fdump << "</dumps>" << std::endl;
|
|
|
|
fdump.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
// run addons
|
|
|
|
executeAddons(dumpFile);
|
|
|
|
|
2020-12-31 17:37:02 +01:00
|
|
|
} catch (const InternalError &e) {
|
|
|
|
internalError(path, e.errorMessage);
|
|
|
|
mExitCode = 1; // e.g. reflect a syntax error
|
|
|
|
} catch (const std::exception &e) {
|
|
|
|
internalError(path, e.what());
|
|
|
|
}
|
|
|
|
|
2020-01-24 18:04:39 +01:00
|
|
|
return mExitCode;
|
2020-01-05 15:12:53 +01:00
|
|
|
}
|
|
|
|
|
2018-04-11 09:50:42 +02:00
|
|
|
std::ifstream fin(path);
|
2018-04-21 13:28:26 +02:00
|
|
|
return checkFile(Path::simplifyPath(path), emptyString, fin);
|
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);
|
2018-04-21 13:28:26 +02:00
|
|
|
return checkFile(Path::simplifyPath(path), emptyString, iss);
|
2010-04-13 19:25:08 +02:00
|
|
|
}
|
|
|
|
|
2016-08-13 10:50:03 +02:00
|
|
|
unsigned int CppCheck::check(const ImportProject::FileSettings &fs)
|
|
|
|
{
|
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 += ';';
|
2020-05-26 20:46: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;
|
2018-10-09 15:05:05 +02:00
|
|
|
temp.mSettings.userUndefs = fs.undefs;
|
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);
|
2020-02-02 14:36:33 +01:00
|
|
|
if (fs.platformType != Settings::Unspecified)
|
2018-06-16 16:10:28 +02:00
|
|
|
temp.mSettings.platform(fs.platformType);
|
2020-02-02 16:35:42 +01:00
|
|
|
if (mSettings.clang) {
|
|
|
|
temp.mSettings.includePaths.insert(temp.mSettings.includePaths.end(), fs.systemIncludePaths.cbegin(), fs.systemIncludePaths.cend());
|
2020-12-29 12:43:21 +01:00
|
|
|
return temp.check(Path::simplifyPath(fs.filename));
|
2020-02-02 16:35:42 +01:00
|
|
|
}
|
2018-04-11 09:50:42 +02:00
|
|
|
std::ifstream fin(fs.filename);
|
2020-11-04 21:01:48 +01:00
|
|
|
unsigned int returnValue = temp.checkFile(Path::simplifyPath(fs.filename), fs.cfg, fin);
|
|
|
|
mSettings.nomsg.addSuppressions(temp.mSettings.nomsg.getSuppressions());
|
|
|
|
return returnValue;
|
2016-08-13 10:50:03 +02:00
|
|
|
}
|
|
|
|
|
2018-04-21 13:28:26 +02:00
|
|
|
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;
|
2018-09-08 11:09:49 +02:00
|
|
|
mSuppressInternalErrorFound = false;
|
2010-04-13 19:25:08 +02:00
|
|
|
|
2012-10-03 19:51:09 +02:00
|
|
|
// only show debug warnings for accepted C/C++ source files
|
2013-10-31 19:09:01 +01:00
|
|
|
if (!Path::acceptFile(filename))
|
2018-06-16 16:10:28 +02:00
|
|
|
mSettings.debugwarnings = false;
|
2012-01-06 16:08:08 +01:00
|
|
|
|
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
|
|
|
|
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);
|
2018-06-16 16:10:28 +02:00
|
|
|
mErrorLogger.reportOut(std::string("Checking ") + fixedpath + ' ' + cfgname + std::string("..."));
|
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);
|
|
|
|
mErrorLogger.reportOut(std::string("Platform:") + mSettings.platformString());
|
2016-08-13 10:50:03 +02:00
|
|
|
}
|
2011-04-24 18:10:25 +02:00
|
|
|
}
|
2010-04-13 19:25:08 +02:00
|
|
|
|
2017-05-16 14:07:23 +02:00
|
|
|
if (plistFile.is_open()) {
|
|
|
|
plistFile << ErrorLogger::plistFooter();
|
|
|
|
plistFile.close();
|
|
|
|
}
|
|
|
|
|
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 {
|
2018-06-16 16:10:28 +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;
|
2016-07-21 07:48:17 +02:00
|
|
|
simplecpp::TokenList tokens1(fileStream, files, filename, &outputList);
|
2017-05-28 20:34:58 +02:00
|
|
|
|
|
|
|
// If there is a syntax error, report it and stop
|
2018-11-05 09:49:28 +01:00
|
|
|
for (const simplecpp::Output &output : outputList) {
|
2017-09-12 22:42:10 +02:00
|
|
|
bool err;
|
2018-11-05 09:49:28 +01:00
|
|
|
switch (output.type) {
|
2017-09-12 22:42:10 +02:00
|
|
|
case simplecpp::Output::ERROR:
|
|
|
|
case simplecpp::Output::INCLUDE_NESTED_TOO_DEEPLY:
|
|
|
|
case simplecpp::Output::SYNTAX_ERROR:
|
|
|
|
case simplecpp::Output::UNHANDLED_CHAR_ERROR:
|
2019-12-09 19:16:55 +01:00
|
|
|
case simplecpp::Output::EXPLICIT_INCLUDE_NOT_FOUND:
|
2017-09-12 22:42:10 +02:00
|
|
|
err = true;
|
|
|
|
break;
|
|
|
|
case simplecpp::Output::WARNING:
|
|
|
|
case simplecpp::Output::MISSING_HEADER:
|
|
|
|
case simplecpp::Output::PORTABILITY_BACKSLASH:
|
|
|
|
err = false;
|
|
|
|
break;
|
2019-12-17 08:40:59 +01:00
|
|
|
}
|
2017-09-12 22:42:10 +02:00
|
|
|
|
|
|
|
if (err) {
|
2021-02-18 22:35:55 +01:00
|
|
|
std::string file = Path::fromNativeSeparators(output.location.file());
|
|
|
|
if (mSettings.relativePaths)
|
|
|
|
file = Path::getRelativePath(file, mSettings.basePaths);
|
|
|
|
|
|
|
|
const ErrorMessage::FileLocation loc1(file, output.location.line, output.location.col);
|
2020-05-23 07:16:49 +02:00
|
|
|
std::list<ErrorMessage::FileLocation> callstack(1, loc1);
|
|
|
|
|
|
|
|
ErrorMessage errmsg(callstack,
|
|
|
|
"",
|
|
|
|
Severity::error,
|
|
|
|
output.msg,
|
|
|
|
"syntaxError",
|
2021-02-24 22:00:06 +01:00
|
|
|
Certainty::normal);
|
2018-08-17 10:05:25 +02:00
|
|
|
reportErr(errmsg);
|
|
|
|
return mExitCode;
|
2017-09-12 22:42:10 +02:00
|
|
|
}
|
2017-05-28 20:34:58 +02:00
|
|
|
}
|
|
|
|
|
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;
|
2020-11-13 15:52:57 +01:00
|
|
|
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";
|
2017-05-16 14:07:23 +02:00
|
|
|
plistFile.open(filename2);
|
|
|
|
plistFile << ErrorLogger::plistHeader(version(), files);
|
|
|
|
}
|
|
|
|
|
2017-04-14 10:46:35 +02:00
|
|
|
// write dump file xml prolog
|
|
|
|
std::ofstream fdump;
|
2019-04-07 17:01:59 +02:00
|
|
|
std::string dumpFile;
|
2021-02-06 19:06:05 +01:00
|
|
|
createDumpFile(mSettings, filename, files, tokens1.cfront(), fdump, dumpFile);
|
2017-04-14 10:46:35 +02:00
|
|
|
|
2016-07-21 07:48:17 +02:00
|
|
|
// Parse comments and then remove them
|
|
|
|
preprocessor.inlineSuppressions(tokens1);
|
2019-04-07 17:01:59 +02:00
|
|
|
if ((mSettings.dump || !mSettings.addons.empty()) && fdump.is_open()) {
|
2018-06-16 16:10:28 +02:00
|
|
|
mSettings.nomsg.dump(fdump);
|
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
|
|
|
|
|
|
|
// Calculate checksum so it can be compared with old checksum / future checksums
|
2018-08-09 09:26:54 +02:00
|
|
|
const unsigned int checksum = preprocessor.calculateChecksum(tokens1, toolinfo.str());
|
2020-05-23 07:16:49 +02:00
|
|
|
std::list<ErrorMessage> errors;
|
2018-06-17 07:31:34 +02:00
|
|
|
if (!mAnalyzerInformation.analyzeFile(mSettings.buildDir, filename, cfgname, checksum, &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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-12-11 10:22:06 +01:00
|
|
|
// Run define rules on raw code
|
2018-06-16 16:10:28 +02:00
|
|
|
for (const Settings::Rule &rule : mSettings.rules) {
|
2018-05-22 23:22:46 +02:00
|
|
|
if (rule.tokenlist != "define")
|
2016-07-20 12:21:00 +02:00
|
|
|
continue;
|
|
|
|
|
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) {
|
|
|
|
if (dir.str.compare(0,8,"#define ") == 0)
|
|
|
|
code += "#line " + MathLib::toString(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);
|
|
|
|
break;
|
2011-04-24 18:10:25 +02:00
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
2014-09-02 18:05:02 +02:00
|
|
|
std::set<unsigned long long> checksums;
|
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, ";")) {
|
|
|
|
if (std::find(v1.begin(), v1.end(), cfg) == v1.end()) {
|
|
|
|
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
|
|
|
|
2016-01-02 11:48:36 +01:00
|
|
|
if (codeWithoutCfg.compare(0,5,"#file") == 0)
|
|
|
|
codeWithoutCfg.insert(0U, "//");
|
|
|
|
std::string::size_type pos = 0;
|
|
|
|
while ((pos = codeWithoutCfg.find("\n#file",pos)) != std::string::npos)
|
|
|
|
codeWithoutCfg.insert(pos+1U, "//");
|
|
|
|
pos = 0;
|
|
|
|
while ((pos = codeWithoutCfg.find("\n#endfile",pos)) != std::string::npos)
|
|
|
|
codeWithoutCfg.insert(pos+1U, "//");
|
|
|
|
pos = 0;
|
|
|
|
while ((pos = codeWithoutCfg.find(Preprocessor::macroChar,pos)) != std::string::npos)
|
|
|
|
codeWithoutCfg[pos] = ' ';
|
|
|
|
reportOut(codeWithoutCfg);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-06-30 10:59:57 +02:00
|
|
|
Tokenizer tokenizer(&mSettings, this);
|
|
|
|
tokenizer.setPreprocessor(&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
|
|
|
|
|
|
|
// 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);
|
2018-06-17 07:40:13 +02:00
|
|
|
mErrorLogger.reportOut("Checking " + fixedpath + ": " + mCurrentConfig + "...");
|
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
|
2019-11-20 15:37:09 +01:00
|
|
|
Timer timer2("Tokenizer::simplifyTokens1", mSettings.showtime, &s_timerResults);
|
2020-06-30 10:59:57 +02:00
|
|
|
bool result = tokenizer.simplifyTokens1(mCurrentConfig);
|
2019-11-20 15:37:09 +01:00
|
|
|
timer2.stop();
|
2015-12-11 10:22:06 +01:00
|
|
|
if (!result)
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
// Skip if we already met the same simplified token list
|
2018-06-16 16:10:28 +02:00
|
|
|
if (mSettings.force || mSettings.maxConfigs > 1) {
|
2020-06-30 10:59:57 +02:00
|
|
|
const unsigned long long checksum = tokenizer.list.calculateChecksum();
|
2016-08-21 12:31:26 +02:00
|
|
|
if (checksums.find(checksum) != checksums.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
|
|
|
}
|
2015-12-11 10:22:06 +01:00
|
|
|
checksums.insert(checksum);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
|
|
|
2015-12-11 10:22:06 +01:00
|
|
|
// simplify more if required, skip rest of iteration if failed
|
2020-05-30 11:23:22 +02:00
|
|
|
if (mSimplify && hasRule("simple")) {
|
|
|
|
std::cout << "Handling of \"simple\" rules is deprecated and will be removed in Cppcheck 2.5." << std::endl;
|
|
|
|
|
|
|
|
// if further simplification fails then skip rest of iteration
|
|
|
|
Timer timer3("Tokenizer::simplifyTokenList2", mSettings.showtime, &s_timerResults);
|
2020-06-30 10:59:57 +02:00
|
|
|
result = tokenizer.simplifyTokenList2();
|
2020-05-30 11:23:22 +02:00
|
|
|
timer3.stop();
|
|
|
|
if (!result)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!Settings::terminated())
|
2020-06-30 10:59:57 +02:00
|
|
|
executeRules("simple", tokenizer);
|
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
|
2018-06-17 07:40:13 +02:00
|
|
|
configurationError.push_back((mCurrentConfig.empty() ? "\'\'" : mCurrentConfig) + " : [" + o.location.file() + ':' + MathLib::toString(o.location.line) + "] " + o.msg);
|
2018-05-15 15:42:54 +02:00
|
|
|
--checkCount; // don't count invalid configurations
|
|
|
|
continue;
|
|
|
|
|
2015-12-11 10:22:06 +01:00
|
|
|
} catch (const InternalError &e) {
|
2020-05-23 07:16:49 +02:00
|
|
|
std::list<ErrorMessage::FileLocation> locationList;
|
2015-12-11 10:22:06 +01:00
|
|
|
if (e.token) {
|
2020-06-30 10:59:57 +02:00
|
|
|
ErrorMessage::FileLocation loc(e.token, &tokenizer.list);
|
2019-08-18 16:33:32 +02:00
|
|
|
locationList.push_back(loc);
|
2015-12-11 10:22:06 +01:00
|
|
|
} else {
|
2020-06-30 10:59:57 +02:00
|
|
|
ErrorMessage::FileLocation loc(tokenizer.list.getSourceFilePath(), 0, 0);
|
2020-05-23 07:16:49 +02:00
|
|
|
ErrorMessage::FileLocation loc2(filename, 0, 0);
|
2015-12-11 10:22:06 +01:00
|
|
|
locationList.push_back(loc2);
|
2019-08-18 16:33:32 +02:00
|
|
|
locationList.push_back(loc);
|
2015-12-11 10:22:06 +01:00
|
|
|
}
|
2020-05-23 07:16:49 +02:00
|
|
|
ErrorMessage errmsg(locationList,
|
2020-06-30 10:59:57 +02:00
|
|
|
tokenizer.list.getSourceFilePath(),
|
2020-05-23 07:16:49 +02:00
|
|
|
Severity::error,
|
|
|
|
e.errorMessage,
|
|
|
|
e.id,
|
2021-02-24 22:00:06 +01:00
|
|
|
Certainty::normal);
|
2015-12-11 10:22:06 +01:00
|
|
|
|
2021-02-24 22:00:06 +01:00
|
|
|
if (errmsg.severity == Severity::error || mSettings.severity.isEnabled(errmsg.severity))
|
2018-11-13 20:14:56 +01: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
|
|
|
|
2020-05-23 07:16:49 +02:00
|
|
|
std::list<ErrorMessage::FileLocation> locationList;
|
|
|
|
ErrorMessage::FileLocation loc;
|
2018-05-06 22:02:23 +02:00
|
|
|
loc.setfile(Path::toNativeSeparators(filename));
|
|
|
|
locationList.push_back(loc);
|
2020-05-23 07:16:49 +02:00
|
|
|
ErrorMessage errmsg(locationList,
|
|
|
|
loc.getfile(),
|
|
|
|
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);
|
|
|
|
|
2012-04-16 16:25:04 +02:00
|
|
|
} catch (const std::runtime_error &e) {
|
2013-05-09 18:50:24 +02:00
|
|
|
internalError(filename, e.what());
|
2016-05-28 13:24:19 +02:00
|
|
|
} catch (const std::bad_alloc &e) {
|
2016-05-30 09:17:40 +02:00
|
|
|
internalError(filename, e.what());
|
2013-05-09 18:50:24 +02:00
|
|
|
} catch (const InternalError &e) {
|
|
|
|
internalError(filename, e.errorMessage);
|
2018-06-17 07:43:25 +02:00
|
|
|
mExitCode=1; // e.g. reflect a syntax error
|
2011-04-24 18:10:25 +02:00
|
|
|
}
|
|
|
|
|
2018-06-17 07:31:34 +02:00
|
|
|
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
|
2021-02-24 22:00:06 +01:00
|
|
|
if (!mSettings.jointSuppressionReport && (mSettings.severity.isEnabled(Severity::information) || mSettings.checkConfiguration)) {
|
2018-06-16 16:10:28 +02:00
|
|
|
reportUnmatchedSuppressions(mSettings.nomsg.getUnmatchedLocalSuppressions(filename, isUnusedFunctionCheckEnabled()));
|
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
|
|
|
|
2018-06-17 07:43:25 +02:00
|
|
|
return mExitCode;
|
2011-07-25 13:25:09 +02:00
|
|
|
}
|
|
|
|
|
2013-05-09 18:50:24 +02:00
|
|
|
void CppCheck::internalError(const std::string &filename, const std::string &msg)
|
|
|
|
{
|
|
|
|
const std::string fixedpath = Path::toNativeSeparators(filename);
|
2015-01-12 23:09:17 +01:00
|
|
|
const std::string fullmsg("Bailing out from checking " + fixedpath + " since there was an internal error: " + msg);
|
2013-05-09 18:50:24 +02:00
|
|
|
|
2021-02-24 22:00:06 +01:00
|
|
|
if (mSettings.severity.isEnabled(Severity::information)) {
|
2020-05-23 07:16:49 +02:00
|
|
|
const ErrorMessage::FileLocation loc1(filename, 0, 0);
|
|
|
|
std::list<ErrorMessage::FileLocation> callstack(1, loc1);
|
2013-05-09 18:50:24 +02:00
|
|
|
|
2020-05-23 07:16:49 +02:00
|
|
|
ErrorMessage errmsg(callstack,
|
|
|
|
emptyString,
|
|
|
|
Severity::information,
|
|
|
|
fullmsg,
|
|
|
|
"internalError",
|
2021-02-24 22:00:06 +01:00
|
|
|
Certainty::normal);
|
2013-05-09 18:50:24 +02:00
|
|
|
|
2018-06-16 16:10:28 +02:00
|
|
|
mErrorLogger.reportErr(errmsg);
|
2013-05-09 18:50:24 +02:00
|
|
|
} else {
|
|
|
|
// Report on stdout
|
2018-06-16 16:10:28 +02:00
|
|
|
mErrorLogger.reportOut(fullmsg);
|
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
|
|
|
{
|
2015-12-11 10:22:06 +01:00
|
|
|
// Execute rules for "raw" code
|
|
|
|
executeRules("raw", tokenizer);
|
|
|
|
}
|
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)
|
|
|
|
{
|
2020-12-28 19:59:51 +01:00
|
|
|
mSettings.library.bugHunting = mSettings.bugHunting;
|
2020-01-14 21:17:07 +01:00
|
|
|
if (mSettings.bugHunting)
|
|
|
|
ExprEngine::runChecks(this, &tokenizer, &mSettings);
|
|
|
|
else {
|
|
|
|
// 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
|
|
|
|
2020-01-14 21:17:07 +01:00
|
|
|
if (Tokenizer::isMaxTime())
|
|
|
|
return;
|
2013-05-11 11:35:04 +02:00
|
|
|
|
2020-01-14 21:17:07 +01:00
|
|
|
Timer timerRunChecks(check->name() + "::runChecks", mSettings.showtime, &s_timerResults);
|
|
|
|
check->runChecks(&tokenizer, &mSettings, this);
|
|
|
|
}
|
2019-09-17 21:00:59 +02:00
|
|
|
|
2020-01-25 17:01:17 +01:00
|
|
|
if (mSettings.clang)
|
|
|
|
// TODO: Use CTU for Clang analysis
|
|
|
|
return;
|
|
|
|
|
2020-01-14 21:17:07 +01:00
|
|
|
// Analyse the tokens..
|
2018-12-25 21:11:23 +01:00
|
|
|
|
2020-01-14 21:17:07 +01:00
|
|
|
CTU::FileInfo *fi1 = CTU::getFileInfo(&tokenizer);
|
|
|
|
if (fi1) {
|
|
|
|
mFileInfo.push_back(fi1);
|
|
|
|
mAnalyzerInformation.setFileInfo("ctu", fi1->toString());
|
|
|
|
}
|
2018-12-25 21:11:23 +01:00
|
|
|
|
2020-01-14 21:17:07 +01:00
|
|
|
for (const Check *check : Check::instances()) {
|
|
|
|
Check::FileInfo *fi = check->getFileInfo(&tokenizer, &mSettings);
|
|
|
|
if (fi != nullptr) {
|
|
|
|
mFileInfo.push_back(fi);
|
|
|
|
mAnalyzerInformation.setFileInfo(check->name(), fi->toString());
|
|
|
|
}
|
2016-10-29 12:18:11 +02:00
|
|
|
}
|
2010-04-13 19:25:08 +02:00
|
|
|
|
2020-01-14 21:17:07 +01:00
|
|
|
executeRules("normal", tokenizer);
|
|
|
|
}
|
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
|
|
|
|
2019-03-17 08:19:56 +01:00
|
|
|
bool CppCheck::hasRule(const std::string &tokenlist) const
|
2015-12-11 10:22:06 +01:00
|
|
|
{
|
2019-03-17 08:19:56 +01:00
|
|
|
#ifdef HAVE_RULES
|
|
|
|
for (const Settings::Rule &rule : mSettings.rules) {
|
|
|
|
if (rule.tokenlist == tokenlist)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
(void)tokenlist;
|
|
|
|
#endif
|
|
|
|
return false;
|
2010-04-13 19:25:08 +02:00
|
|
|
}
|
|
|
|
|
2019-03-17 08:19:56 +01:00
|
|
|
|
2018-09-23 14:27:46 +02:00
|
|
|
#ifdef HAVE_RULES
|
|
|
|
|
|
|
|
static const char * pcreErrorCodeToString(const int pcreExecRet)
|
|
|
|
{
|
|
|
|
switch (pcreExecRet) {
|
|
|
|
case PCRE_ERROR_NULL:
|
|
|
|
return "Either code or subject was passed as NULL, or ovector was NULL "
|
|
|
|
"and ovecsize was not zero (PCRE_ERROR_NULL)";
|
|
|
|
case PCRE_ERROR_BADOPTION:
|
|
|
|
return "An unrecognized bit was set in the options argument (PCRE_ERROR_BADOPTION)";
|
|
|
|
case PCRE_ERROR_BADMAGIC:
|
|
|
|
return "PCRE stores a 4-byte \"magic number\" at the start of the compiled code, "
|
|
|
|
"to catch the case when it is passed a junk pointer and to detect when a "
|
|
|
|
"pattern that was compiled in an environment of one endianness is run in "
|
|
|
|
"an environment with the other endianness. This is the error that PCRE "
|
|
|
|
"gives when the magic number is not present (PCRE_ERROR_BADMAGIC)";
|
|
|
|
case PCRE_ERROR_UNKNOWN_NODE:
|
|
|
|
return "While running the pattern match, an unknown item was encountered in the "
|
|
|
|
"compiled pattern. This error could be caused by a bug in PCRE or by "
|
|
|
|
"overwriting of the compiled pattern (PCRE_ERROR_UNKNOWN_NODE)";
|
|
|
|
case PCRE_ERROR_NOMEMORY:
|
|
|
|
return "If a pattern contains back references, but the ovector that is passed "
|
|
|
|
"to pcre_exec() is not big enough to remember the referenced substrings, "
|
|
|
|
"PCRE gets a block of memory at the start of matching to use for this purpose. "
|
|
|
|
"If the call via pcre_malloc() fails, this error is given. The memory is "
|
|
|
|
"automatically freed at the end of matching. This error is also given if "
|
|
|
|
"pcre_stack_malloc() fails in pcre_exec(). "
|
|
|
|
"This can happen only when PCRE has been compiled with "
|
|
|
|
"--disable-stack-for-recursion (PCRE_ERROR_NOMEMORY)";
|
|
|
|
case PCRE_ERROR_NOSUBSTRING:
|
|
|
|
return "This error is used by the pcre_copy_substring(), pcre_get_substring(), "
|
|
|
|
"and pcre_get_substring_list() functions (see below). "
|
|
|
|
"It is never returned by pcre_exec() (PCRE_ERROR_NOSUBSTRING)";
|
|
|
|
case PCRE_ERROR_MATCHLIMIT:
|
|
|
|
return "The backtracking limit, as specified by the match_limit field in a pcre_extra "
|
|
|
|
"structure (or defaulted) was reached. "
|
|
|
|
"See the description above (PCRE_ERROR_MATCHLIMIT)";
|
|
|
|
case PCRE_ERROR_CALLOUT:
|
|
|
|
return "This error is never generated by pcre_exec() itself. "
|
|
|
|
"It is provided for use by callout functions that want to yield a distinctive "
|
|
|
|
"error code. See the pcrecallout documentation for details (PCRE_ERROR_CALLOUT)";
|
|
|
|
case PCRE_ERROR_BADUTF8:
|
|
|
|
return "A string that contains an invalid UTF-8 byte sequence was passed as a subject, "
|
|
|
|
"and the PCRE_NO_UTF8_CHECK option was not set. If the size of the output vector "
|
|
|
|
"(ovecsize) is at least 2, the byte offset to the start of the the invalid UTF-8 "
|
|
|
|
"character is placed in the first element, and a reason code is placed in the "
|
|
|
|
"second element. The reason codes are listed in the following section. For "
|
|
|
|
"backward compatibility, if PCRE_PARTIAL_HARD is set and the problem is a truncated "
|
|
|
|
"UTF-8 character at the end of the subject (reason codes 1 to 5), "
|
|
|
|
"PCRE_ERROR_SHORTUTF8 is returned instead of PCRE_ERROR_BADUTF8";
|
|
|
|
case PCRE_ERROR_BADUTF8_OFFSET:
|
|
|
|
return "The UTF-8 byte sequence that was passed as a subject was checked and found to "
|
|
|
|
"be valid (the PCRE_NO_UTF8_CHECK option was not set), but the value of "
|
|
|
|
"startoffset did not point to the beginning of a UTF-8 character or the end of "
|
|
|
|
"the subject (PCRE_ERROR_BADUTF8_OFFSET)";
|
|
|
|
case PCRE_ERROR_PARTIAL:
|
|
|
|
return "The subject string did not match, but it did match partially. See the "
|
|
|
|
"pcrepartial documentation for details of partial matching (PCRE_ERROR_PARTIAL)";
|
|
|
|
case PCRE_ERROR_BADPARTIAL:
|
|
|
|
return "This code is no longer in use. It was formerly returned when the PCRE_PARTIAL "
|
|
|
|
"option was used with a compiled pattern containing items that were not supported "
|
|
|
|
"for partial matching. From release 8.00 onwards, there are no restrictions on "
|
|
|
|
"partial matching (PCRE_ERROR_BADPARTIAL)";
|
|
|
|
case PCRE_ERROR_INTERNAL:
|
|
|
|
return "An unexpected internal error has occurred. This error could be caused by a bug "
|
|
|
|
"in PCRE or by overwriting of the compiled pattern (PCRE_ERROR_INTERNAL)";
|
|
|
|
case PCRE_ERROR_BADCOUNT:
|
|
|
|
return"This error is given if the value of the ovecsize argument is negative "
|
|
|
|
"(PCRE_ERROR_BADCOUNT)";
|
|
|
|
case PCRE_ERROR_RECURSIONLIMIT :
|
|
|
|
return "The internal recursion limit, as specified by the match_limit_recursion "
|
|
|
|
"field in a pcre_extra structure (or defaulted) was reached. "
|
|
|
|
"See the description above (PCRE_ERROR_RECURSIONLIMIT)";
|
|
|
|
case PCRE_ERROR_DFA_UITEM:
|
|
|
|
return "PCRE_ERROR_DFA_UITEM";
|
|
|
|
case PCRE_ERROR_DFA_UCOND:
|
|
|
|
return "PCRE_ERROR_DFA_UCOND";
|
|
|
|
case PCRE_ERROR_DFA_WSSIZE:
|
|
|
|
return "PCRE_ERROR_DFA_WSSIZE";
|
|
|
|
case PCRE_ERROR_DFA_RECURSE:
|
|
|
|
return "PCRE_ERROR_DFA_RECURSE";
|
|
|
|
case PCRE_ERROR_NULLWSLIMIT:
|
|
|
|
return "PCRE_ERROR_NULLWSLIMIT";
|
|
|
|
case PCRE_ERROR_BADNEWLINE:
|
|
|
|
return "An invalid combination of PCRE_NEWLINE_xxx options was "
|
|
|
|
"given (PCRE_ERROR_BADNEWLINE)";
|
|
|
|
case PCRE_ERROR_BADOFFSET:
|
|
|
|
return "The value of startoffset was negative or greater than the length "
|
|
|
|
"of the subject, that is, the value in length (PCRE_ERROR_BADOFFSET)";
|
|
|
|
case PCRE_ERROR_SHORTUTF8:
|
|
|
|
return "This error is returned instead of PCRE_ERROR_BADUTF8 when the subject "
|
|
|
|
"string ends with a truncated UTF-8 character and the PCRE_PARTIAL_HARD option is set. "
|
|
|
|
"Information about the failure is returned as for PCRE_ERROR_BADUTF8. "
|
|
|
|
"It is in fact sufficient to detect this case, but this special error code for "
|
|
|
|
"PCRE_PARTIAL_HARD precedes the implementation of returned information; "
|
|
|
|
"it is retained for backwards compatibility (PCRE_ERROR_SHORTUTF8)";
|
|
|
|
case PCRE_ERROR_RECURSELOOP:
|
|
|
|
return "This error is returned when pcre_exec() detects a recursion loop "
|
|
|
|
"within the pattern. Specifically, it means that either the whole pattern "
|
|
|
|
"or a subpattern has been called recursively for the second time at the same "
|
|
|
|
"position in the subject string. Some simple patterns that might do this "
|
|
|
|
"are detected and faulted at compile time, but more complicated cases, "
|
|
|
|
"in particular mutual recursions between two different subpatterns, "
|
|
|
|
"cannot be detected until run time (PCRE_ERROR_RECURSELOOP)";
|
|
|
|
case PCRE_ERROR_JIT_STACKLIMIT:
|
|
|
|
return "This error is returned when a pattern that was successfully studied "
|
|
|
|
"using a JIT compile option is being matched, but the memory available "
|
|
|
|
"for the just-in-time processing stack is not large enough. See the pcrejit "
|
|
|
|
"documentation for more details (PCRE_ERROR_JIT_STACKLIMIT)";
|
|
|
|
case PCRE_ERROR_BADMODE:
|
|
|
|
return "This error is given if a pattern that was compiled by the 8-bit library "
|
|
|
|
"is passed to a 16-bit or 32-bit library function, or vice versa (PCRE_ERROR_BADMODE)";
|
|
|
|
case PCRE_ERROR_BADENDIANNESS:
|
|
|
|
return "This error is given if a pattern that was compiled and saved is reloaded on a "
|
|
|
|
"host with different endianness. The utility function pcre_pattern_to_host_byte_order() "
|
|
|
|
"can be used to convert such a pattern so that it runs on the new host (PCRE_ERROR_BADENDIANNESS)";
|
|
|
|
case PCRE_ERROR_DFA_BADRESTART:
|
|
|
|
return "PCRE_ERROR_DFA_BADRESTART";
|
2018-09-23 17:04:52 +02:00
|
|
|
#if PCRE_MAJOR >= 8 && PCRE_MINOR >= 32
|
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 "";
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // HAVE_RULES
|
|
|
|
|
|
|
|
|
2013-06-09 14:58:56 +02:00
|
|
|
void CppCheck::executeRules(const std::string &tokenlist, const Tokenizer &tokenizer)
|
|
|
|
{
|
2013-06-12 06:43:52 +02:00
|
|
|
(void)tokenlist;
|
|
|
|
(void)tokenizer;
|
2013-06-09 20:17:26 +02:00
|
|
|
|
|
|
|
#ifdef HAVE_RULES
|
2013-06-09 14:58:56 +02:00
|
|
|
// 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) {
|
|
|
|
reportOut("Processing rule: " + rule.pattern);
|
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
2013-06-09 20:17:26 +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)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (!mSettings.addons.empty() && !dumpFile.empty()) {
|
|
|
|
for (const std::string &addon : mSettings.addons) {
|
|
|
|
struct AddonInfo addonInfo;
|
|
|
|
const std::string &failedToGetAddonInfo = addonInfo.getAddonInfo(addon, mSettings.exename);
|
|
|
|
if (!failedToGetAddonInfo.empty()) {
|
|
|
|
reportOut(failedToGetAddonInfo);
|
|
|
|
mExitCode = 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
const std::string results =
|
|
|
|
executeAddon(addonInfo, mSettings.addonPython, dumpFile, mExecuteCommand);
|
|
|
|
std::istringstream istr(results);
|
|
|
|
std::string line;
|
|
|
|
|
|
|
|
while (std::getline(istr, line)) {
|
|
|
|
if (line.compare(0,1,"{") != 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
picojson::value res;
|
|
|
|
std::istringstream istr2(line);
|
|
|
|
istr2 >> res;
|
|
|
|
if (!res.is<picojson::object>())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
picojson::object obj = res.get<picojson::object>();
|
|
|
|
|
|
|
|
const std::string fileName = obj["file"].get<std::string>();
|
|
|
|
const int64_t lineNumber = obj["linenr"].get<int64_t>();
|
|
|
|
const int64_t column = obj["column"].get<int64_t>();
|
|
|
|
|
|
|
|
ErrorMessage errmsg;
|
|
|
|
|
|
|
|
errmsg.callStack.emplace_back(ErrorMessage::FileLocation(fileName, lineNumber, column));
|
|
|
|
|
|
|
|
errmsg.id = obj["addon"].get<std::string>() + "-" + obj["errorId"].get<std::string>();
|
|
|
|
const std::string text = obj["message"].get<std::string>();
|
|
|
|
errmsg.setmsg(text);
|
|
|
|
const std::string severity = obj["severity"].get<std::string>();
|
|
|
|
errmsg.severity = Severity::fromString(severity);
|
|
|
|
if (errmsg.severity == Severity::SeverityType::none)
|
|
|
|
continue;
|
|
|
|
errmsg.file0 = fileName;
|
|
|
|
|
|
|
|
reportErr(errmsg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
std::remove(dumpFile.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
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()) {
|
2020-05-23 07:16:49 +02:00
|
|
|
ErrorMessage::FileLocation location;
|
2012-12-26 18:35:49 +01:00
|
|
|
location.setfile(file);
|
|
|
|
loclist.push_back(location);
|
|
|
|
}
|
|
|
|
|
|
|
|
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()) {
|
2020-05-23 07:16:49 +02:00
|
|
|
ErrorMessage::FileLocation location;
|
2014-09-02 18:05:02 +02:00
|
|
|
location.setfile(file);
|
|
|
|
loclist.push_back(location);
|
|
|
|
}
|
|
|
|
|
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
|
|
|
{
|
2018-09-08 11:09:49 +02:00
|
|
|
mSuppressInternalErrorFound = false;
|
|
|
|
|
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
|
2018-06-16 23:31:16 +02:00
|
|
|
if (std::find(mErrorList.begin(), mErrorList.end(), errmsg) != mErrorList.end())
|
2012-04-06 18:16:59 +02:00
|
|
|
return;
|
|
|
|
|
2018-04-09 06:43:48 +02:00
|
|
|
const Suppressions::ErrorMessage errorMessage = msg.toSuppressionsErrorMessage();
|
2010-04-13 19:25:08 +02:00
|
|
|
|
2018-06-17 07:29:07 +02:00
|
|
|
if (mUseGlobalSuppressions) {
|
2018-09-08 11:09:49 +02:00
|
|
|
if (mSettings.nomsg.isSuppressed(errorMessage)) {
|
|
|
|
mSuppressInternalErrorFound = true;
|
2018-05-11 09:01:08 +02:00
|
|
|
return;
|
2018-09-08 11:09:49 +02:00
|
|
|
}
|
2018-05-11 09:01:08 +02:00
|
|
|
} else {
|
2018-09-08 11:09:49 +02:00
|
|
|
if (mSettings.nomsg.isSuppressedLocal(errorMessage)) {
|
|
|
|
mSuppressInternalErrorFound = true;
|
2018-05-11 09:01:08 +02:00
|
|
|
return;
|
2018-09-08 11:09:49 +02:00
|
|
|
}
|
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);
|
2018-06-17 07:31:34 +02:00
|
|
|
mAnalyzerInformation.reportErr(msg, mSettings.verbose);
|
2018-06-16 16:10:28 +02:00
|
|
|
if (!mSettings.plistOutput.empty() && plistFile.is_open()) {
|
2017-05-16 14:07:23 +02:00
|
|
|
plistFile << ErrorLogger::plistData(msg);
|
|
|
|
}
|
2010-04-13 19:25:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CppCheck::reportOut(const std::string &outmsg)
|
|
|
|
{
|
2018-06-16 16:10:28 +02:00
|
|
|
mErrorLogger.reportOut(outmsg);
|
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
|
|
|
}
|
|
|
|
|
2020-05-23 07:16:49 +02:00
|
|
|
void CppCheck::reportInfo(const ErrorMessage &msg)
|
2012-06-18 23:15:48 +02:00
|
|
|
{
|
2018-04-09 06:43:48 +02:00
|
|
|
const Suppressions::ErrorMessage &errorMessage = msg.toSuppressionsErrorMessage();
|
2018-06-16 16:10:28 +02:00
|
|
|
if (!mSettings.nomsg.isSuppressed(errorMessage))
|
|
|
|
mErrorLogger.reportInfo(msg);
|
2012-06-18 23:15:48 +02:00
|
|
|
}
|
|
|
|
|
2012-07-08 23:39:46 +02:00
|
|
|
void CppCheck::reportStatus(unsigned int /*fileindex*/, unsigned int /*filecount*/, std::size_t /*sizedone*/, std::size_t /*sizetotal*/)
|
2010-08-03 16:36:21 +02:00
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-01-18 07:25:39 +01:00
|
|
|
void CppCheck::bughuntingReport(const std::string &str)
|
2019-12-27 19:05:10 +01:00
|
|
|
{
|
2020-01-18 07:25:39 +01:00
|
|
|
mErrorLogger.bughuntingReport(str);
|
2019-12-27 19:05:10 +01:00
|
|
|
}
|
|
|
|
|
2010-04-13 19:25:08 +02:00
|
|
|
void CppCheck::getErrorMessages()
|
|
|
|
{
|
2018-06-16 16:10:28 +02:00
|
|
|
Settings s(mSettings);
|
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
|
|
|
|
2018-02-10 22:30:49 +01:00
|
|
|
purgedConfigurationMessage("","");
|
|
|
|
|
2018-06-17 19:20:07 +02:00
|
|
|
mTooManyConfigs = true;
|
2012-12-26 18:35:49 +01:00
|
|
|
tooManyConfigsError("",0U);
|
|
|
|
|
2010-04-13 19:25:08 +02:00
|
|
|
// call all "getErrorMessages" in all registered Check classes
|
2014-04-13 11:51:29 +02:00
|
|
|
for (std::list<Check *>::const_iterator it = Check::instances().begin(); it != Check::instances().end(); ++it)
|
2015-01-06 15:08:25 +01:00
|
|
|
(*it)->getErrorMessages(this, &s);
|
2010-04-13 19:25:08 +02:00
|
|
|
|
2015-01-06 15:08:25 +01:00
|
|
|
Preprocessor::getErrorMessages(this, &s);
|
2010-04-13 19:25:08 +02:00
|
|
|
}
|
2014-11-15 10:43:49 +01:00
|
|
|
|
2020-01-31 14:13:52 +01:00
|
|
|
void CppCheck::analyseClangTidy(const ImportProject::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
|
|
|
|
const char exe[] = "clang-tidy.exe";
|
|
|
|
#else
|
|
|
|
const char exe[] = "clang-tidy";
|
|
|
|
#endif
|
|
|
|
|
|
|
|
const std::string args = "-quiet -checks=*,-clang-analyzer-*,-llvm* \"" + fileSettings.filename + "\" -- " + allIncludes + allDefines;
|
|
|
|
std::string output;
|
|
|
|
if (!mExecuteCommand(exe, split(args), "", &output)) {
|
|
|
|
std::cerr << "Failed to execute '" << exe << "'" << std::endl;
|
2020-01-31 14:13:52 +01:00
|
|
|
return;
|
2020-01-30 07:14:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// parse output and create error messages
|
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()) {
|
2020-01-30 07:14:17 +01:00
|
|
|
const std::string analyzerInfoFile = AnalyzerInformation::getAnalyzerInfoFile(mSettings.buildDir, fileSettings.filename, "");
|
|
|
|
std::ofstream fcmd(analyzerInfoFile + ".clang-tidy-cmd");
|
|
|
|
fcmd << istr.str();
|
|
|
|
}
|
|
|
|
|
|
|
|
while (std::getline(istr, line)) {
|
|
|
|
if (line.find("error") == std::string::npos && line.find("warning") == std::string::npos)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
std::size_t endColumnPos = line.find(": error:");
|
|
|
|
if (endColumnPos == std::string::npos) {
|
|
|
|
endColumnPos = line.find(": warning:");
|
|
|
|
}
|
|
|
|
|
|
|
|
const std::size_t endLinePos = line.rfind(":", endColumnPos-1);
|
|
|
|
const std::size_t endNamePos = line.rfind(":", endLinePos - 1);
|
|
|
|
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 errorTypeString = line.substr(endColumnPos + 1, endMsgTypePos - endColumnPos - 1);
|
|
|
|
const std::string messageString = line.substr(endMsgTypePos + 1, endErrorPos - endMsgTypePos - 1);
|
|
|
|
const std::string errorString = line.substr(endErrorPos, line.length());
|
|
|
|
|
|
|
|
std::string fixedpath = Path::simplifyPath(line.substr(0, endNamePos));
|
|
|
|
const int64_t lineNumber = std::atol(lineNumString.c_str());
|
|
|
|
const int64_t column = std::atol(columnNumString.c_str());
|
|
|
|
fixedpath = Path::toNativeSeparators(fixedpath);
|
|
|
|
|
2020-05-23 07:16:49 +02:00
|
|
|
ErrorMessage errmsg;
|
|
|
|
errmsg.callStack.emplace_back(ErrorMessage::FileLocation(fixedpath, lineNumber, column));
|
2020-01-30 07:14:17 +01:00
|
|
|
|
|
|
|
errmsg.id = "clang-tidy-" + errorString.substr(1, errorString.length() - 2);
|
|
|
|
if (errmsg.id.find("performance") != std::string::npos)
|
|
|
|
errmsg.severity = Severity::SeverityType::performance;
|
|
|
|
else if (errmsg.id.find("portability") != std::string::npos)
|
|
|
|
errmsg.severity = Severity::SeverityType::portability;
|
|
|
|
else if (errmsg.id.find("cert") != std::string::npos || errmsg.id.find("misc") != std::string::npos || errmsg.id.find("unused") != std::string::npos)
|
|
|
|
errmsg.severity = Severity::SeverityType::warning;
|
|
|
|
else
|
|
|
|
errmsg.severity = Severity::SeverityType::style;
|
|
|
|
|
|
|
|
errmsg.file0 = fixedpath;
|
|
|
|
errmsg.setmsg(messageString);
|
|
|
|
reportErr(errmsg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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) {
|
|
|
|
ctu.functionCalls.insert(ctu.functionCalls.end(), fi2->functionCalls.begin(), fi2->functionCalls.end());
|
|
|
|
ctu.nestedCalls.insert(ctu.nestedCalls.end(), fi2->nestedCalls.begin(), fi2->nestedCalls.end());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2016-11-05 21:26:56 +01:00
|
|
|
void CppCheck::analyseWholeProgram(const std::string &buildDir, const std::map<std::string, std::size_t> &files)
|
|
|
|
{
|
2016-12-08 22:46:44 +01:00
|
|
|
(void)files;
|
2016-11-05 21:26:56 +01:00
|
|
|
if (buildDir.empty())
|
|
|
|
return;
|
2021-02-24 22:00:06 +01:00
|
|
|
if (mSettings.checks.isEnabled(Checks::unusedFunction))
|
2016-12-08 22:46:44 +01:00
|
|
|
CheckUnusedFunctions::analyseWholeProgram(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;
|
|
|
|
}
|
|
|
|
for (Check *check : Check::instances()) {
|
|
|
|
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
|
|
|
{
|
2021-02-24 22:00:06 +01:00
|
|
|
return (mSettings.jobs == 1 && mSettings.checks.isEnabled(Checks::unusedFunction));
|
2015-01-07 19:26:16 +01:00
|
|
|
}
|