cppcheck/lib/errorlogger.cpp

839 lines
30 KiB
C++
Raw Normal View History

2009-01-09 20:01:25 +01:00
/*
* Cppcheck - A tool for static C/C++ code analysis
2021-03-21 20:58:32 +01:00
* Copyright (C) 2007-2021 Cppcheck team.
2009-01-09 20:01:25 +01: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/>.
2009-01-09 20:01:25 +01:00
*/
2009-02-01 16:47:36 +01:00
#include "errorlogger.h"
2017-05-27 04:33:47 +02:00
2010-12-22 10:29:23 +01:00
#include "cppcheck.h"
2017-05-27 04:33:47 +02:00
#include "mathlib.h"
#include "path.h"
#include "token.h"
2017-05-27 04:33:47 +02:00
#include "tokenlist.h"
2016-02-27 16:03:50 +01:00
#include "utils.h"
2013-08-07 16:30:55 +02:00
#include <tinyxml2.h>
2017-05-27 04:33:47 +02:00
#include <array>
#include <cassert>
2017-05-27 04:33:47 +02:00
#include <cctype>
#include <cstdlib>
#include <cstring>
#include <iomanip>
2009-02-01 16:47:36 +01:00
InternalError::InternalError(const Token *tok, const std::string &errorMsg, Type type) :
token(tok), errorMessage(errorMsg), type(type)
{
switch (type) {
case AST:
id = "internalAstError";
break;
case SYNTAX:
id = "syntaxError";
break;
case UNKNOWN_MACRO:
id = "unknownMacro";
break;
case INTERNAL:
id = "cppcheckError";
break;
case LIMIT:
id = "cppcheckLimit";
break;
case INSTANTIATION:
id = "instantiationError";
break;
}
}
2020-07-21 11:27:03 +02:00
static std::size_t calculateWarningHash(const TokenList *tokenList, const std::string &msg)
{
if (!tokenList)
return 0;
return std::hash<std::string> {}(msg + "\n" + tokenList->front()->stringifyList(false, true, false, false, false));
}
2020-05-23 07:16:49 +02:00
ErrorMessage::ErrorMessage()
: incomplete(false), severity(Severity::none), cwe(0U), certainty(Certainty::normal), hash(0)
{
}
2010-07-27 09:32:20 +02:00
ErrorMessage::ErrorMessage(const std::list<FileLocation> &callStack, const std::string& file1, Severity::SeverityType severity, const std::string &msg, const std::string &id, Certainty::CertaintyLevel certainty) :
callStack(callStack), // locations for this error message
id(id), // set the message id
lib: fix a bunch of warnings about differing function arguments in definition and declaration. [lib/token.h:72] -> [lib/token.cpp:36]: (style, inconclusive) Function 'Token' argument 1 names different: declaration 'tokensBack' definition 't'. [lib/token.h:445] -> [lib/token.cpp:497]: (style, inconclusive) Function 'multiCompare' argument 1 names different: declaration 'needle' definition 'tok'. [lib/checkio.h:73] -> [lib/checkio.cpp:1385]: (style, inconclusive) Function 'ArgumentInfo' argument 3 names different: declaration 'isCPP' definition '_isCPP'. [lib/checkother.h:216] -> [lib/checkother.cpp:2136]: (style, inconclusive) Function 'checkComparisonFunctionIsAlwaysTrueOrFalseError' argument 2 names different: declaration 'strFunctionName' definition 'functionName'. [lib/errorlogger.h:214] -> [lib/errorlogger.cpp:51]: (style, inconclusive) Function 'ErrorMessage' argument 2 names different: declaration 'file0' definition 'file0_'. [lib/errorlogger.h:215] -> [lib/errorlogger.cpp:65]: (style, inconclusive) Function 'ErrorMessage' argument 2 names different: declaration 'file0' definition 'file0_'. [lib/library.h:327] -> [lib/library.cpp:1043]: (style, inconclusive) Function 'ignorefunction' argument 1 names different: declaration 'function' definition 'functionName'. [lib/mathlib.h:112] -> [lib/mathlib.cpp:1275]: (style, inconclusive) Function 'isNullValue' argument 1 names different: declaration 'tok' definition 'str'. [lib/preprocessor.h:91] -> [lib/preprocessor.cpp:122]: (style, inconclusive) Function 'setDirectives' argument 1 names different: declaration 'tokens' definition 'tokens1'. [lib/symboldatabase.h:860] -> [lib/symboldatabase.cpp:1801]: (style, inconclusive) Function 'argsMatch' argument 1 names different: declaration 'info' definition 'scope'. [lib/symboldatabase.h:1171] -> [lib/symboldatabase.cpp:2048]: (style, inconclusive) Function 'addClassFunction' argument 1 names different: declaration 'info' definition 'scope'. [lib/symboldatabase.h:1174] -> [lib/symboldatabase.cpp:2208]: (style, inconclusive) Function 'addNewFunction' argument 1 names different: declaration 'info' definition 'scope'. [lib/symboldatabase.h:1090] -> [lib/symboldatabase.cpp:3648]: (style, inconclusive) Function 'findVariableType' argument 2 names different: declaration 'type' definition 'typeTok'. [lib/symboldatabase.h:1101] -> [lib/symboldatabase.cpp:4308]: (style, inconclusive) Function 'findType' argument 1 names different: declaration 'tok' definition 'startTok'. [lib/symboldatabase.h:1176] -> [lib/symboldatabase.cpp:4349]: (style, inconclusive) Function 'findTypeInNested' argument 1 names different: declaration 'tok' definition 'startTok'. [lib/symboldatabase.h:1193] -> [lib/symboldatabase.cpp:4501]: (style, inconclusive) Function 'setValueType' argument 2 names different: declaration 'enumerators' definition 'enumerator'. [lib/path.h:159] -> [lib/path.cpp:247]: (style, inconclusive) Function 'isCPP' argument 1 names different: declaration 'extensionInLowerCase' definition 'path'. [lib/path.h:145] -> [lib/path.cpp:266]: (style, inconclusive) Function 'acceptFile' argument 1 names different: declaration 'filename' definition 'path'.
2017-04-03 00:06:46 +02:00
file0(file1),
incomplete(false),
2020-05-03 20:46:29 +02:00
severity(severity), // severity for this error message
cwe(0U),
certainty(certainty),
2020-07-21 11:27:03 +02:00
hash(0)
{
2010-12-30 22:07:27 +01:00
// set the summary and verbose messages
setmsg(msg);
}
ErrorMessage::ErrorMessage(const std::list<FileLocation> &callStack, const std::string& file1, Severity::SeverityType severity, const std::string &msg, const std::string &id, const CWE &cwe, Certainty::CertaintyLevel certainty) :
callStack(callStack), // locations for this error message
id(id), // set the message id
lib: fix a bunch of warnings about differing function arguments in definition and declaration. [lib/token.h:72] -> [lib/token.cpp:36]: (style, inconclusive) Function 'Token' argument 1 names different: declaration 'tokensBack' definition 't'. [lib/token.h:445] -> [lib/token.cpp:497]: (style, inconclusive) Function 'multiCompare' argument 1 names different: declaration 'needle' definition 'tok'. [lib/checkio.h:73] -> [lib/checkio.cpp:1385]: (style, inconclusive) Function 'ArgumentInfo' argument 3 names different: declaration 'isCPP' definition '_isCPP'. [lib/checkother.h:216] -> [lib/checkother.cpp:2136]: (style, inconclusive) Function 'checkComparisonFunctionIsAlwaysTrueOrFalseError' argument 2 names different: declaration 'strFunctionName' definition 'functionName'. [lib/errorlogger.h:214] -> [lib/errorlogger.cpp:51]: (style, inconclusive) Function 'ErrorMessage' argument 2 names different: declaration 'file0' definition 'file0_'. [lib/errorlogger.h:215] -> [lib/errorlogger.cpp:65]: (style, inconclusive) Function 'ErrorMessage' argument 2 names different: declaration 'file0' definition 'file0_'. [lib/library.h:327] -> [lib/library.cpp:1043]: (style, inconclusive) Function 'ignorefunction' argument 1 names different: declaration 'function' definition 'functionName'. [lib/mathlib.h:112] -> [lib/mathlib.cpp:1275]: (style, inconclusive) Function 'isNullValue' argument 1 names different: declaration 'tok' definition 'str'. [lib/preprocessor.h:91] -> [lib/preprocessor.cpp:122]: (style, inconclusive) Function 'setDirectives' argument 1 names different: declaration 'tokens' definition 'tokens1'. [lib/symboldatabase.h:860] -> [lib/symboldatabase.cpp:1801]: (style, inconclusive) Function 'argsMatch' argument 1 names different: declaration 'info' definition 'scope'. [lib/symboldatabase.h:1171] -> [lib/symboldatabase.cpp:2048]: (style, inconclusive) Function 'addClassFunction' argument 1 names different: declaration 'info' definition 'scope'. [lib/symboldatabase.h:1174] -> [lib/symboldatabase.cpp:2208]: (style, inconclusive) Function 'addNewFunction' argument 1 names different: declaration 'info' definition 'scope'. [lib/symboldatabase.h:1090] -> [lib/symboldatabase.cpp:3648]: (style, inconclusive) Function 'findVariableType' argument 2 names different: declaration 'type' definition 'typeTok'. [lib/symboldatabase.h:1101] -> [lib/symboldatabase.cpp:4308]: (style, inconclusive) Function 'findType' argument 1 names different: declaration 'tok' definition 'startTok'. [lib/symboldatabase.h:1176] -> [lib/symboldatabase.cpp:4349]: (style, inconclusive) Function 'findTypeInNested' argument 1 names different: declaration 'tok' definition 'startTok'. [lib/symboldatabase.h:1193] -> [lib/symboldatabase.cpp:4501]: (style, inconclusive) Function 'setValueType' argument 2 names different: declaration 'enumerators' definition 'enumerator'. [lib/path.h:159] -> [lib/path.cpp:247]: (style, inconclusive) Function 'isCPP' argument 1 names different: declaration 'extensionInLowerCase' definition 'path'. [lib/path.h:145] -> [lib/path.cpp:266]: (style, inconclusive) Function 'acceptFile' argument 1 names different: declaration 'filename' definition 'path'.
2017-04-03 00:06:46 +02:00
file0(file1),
incomplete(false),
2020-05-03 20:46:29 +02:00
severity(severity), // severity for this error message
cwe(cwe.id),
certainty(certainty),
2020-07-21 11:27:03 +02:00
hash(0)
{
// set the summary and verbose messages
setmsg(msg);
}
ErrorMessage::ErrorMessage(const std::list<const Token*>& callstack, const TokenList* list, Severity::SeverityType severity, const std::string& id, const std::string& msg, Certainty::CertaintyLevel certainty)
: id(id), incomplete(false), severity(severity), cwe(0U), certainty(certainty), hash(0)
{
// Format callstack
for (std::list<const Token *>::const_iterator it = callstack.begin(); it != callstack.end(); ++it) {
// --errorlist can provide null values here
if (!(*it))
continue;
callStack.emplace_back(*it, list);
}
if (list && !list->getFiles().empty())
file0 = list->getFiles()[0];
setmsg(msg);
}
ErrorMessage::ErrorMessage(const std::list<const Token*>& callstack, const TokenList* list, Severity::SeverityType severity, const std::string& id, const std::string& msg, const CWE &cwe, Certainty::CertaintyLevel certainty, bool bugHunting)
: id(id), incomplete(false), severity(severity), cwe(cwe.id), certainty(certainty)
{
// Format callstack
2020-01-05 15:12:53 +01:00
for (const Token *tok: callstack) {
// --errorlist can provide null values here
2020-01-05 15:12:53 +01:00
if (!tok)
continue;
2020-01-05 15:12:53 +01:00
callStack.emplace_back(tok, list);
}
if (list && !list->getFiles().empty())
file0 = list->getFiles()[0];
setmsg(msg);
2020-07-21 15:59:48 +02:00
std::ostringstream hashWarning;
for (const Token *tok: callstack)
hashWarning << std::hex << (tok ? tok->index() : 0) << " ";
hashWarning << mShortMessage;
hash = bugHunting ? calculateWarningHash(list, hashWarning.str()) : 0;
}
ErrorMessage::ErrorMessage(const ErrorPath &errorPath, const TokenList *tokenList, Severity::SeverityType severity, const char id[], const std::string &msg, const CWE &cwe, Certainty::CertaintyLevel certainty, bool bugHunting)
: id(id), incomplete(false), severity(severity), cwe(cwe.id), certainty(certainty)
{
// Format callstack
2020-07-21 15:59:48 +02:00
for (const ErrorPathItem& e: errorPath) {
const Token *tok = e.first;
2020-09-25 19:04:22 +02:00
std::string info = e.second;
if (info.compare(0,8,"$symbol:") == 0 && info.find("\n") < info.size()) {
const std::string::size_type pos = info.find("\n");
const std::string &symbolName = info.substr(8, pos - 8);
info = replaceStr(info.substr(pos+1), "$symbol", symbolName);
}
// --errorlist can provide null values here
if (tok)
callStack.emplace_back(tok, info, tokenList);
}
if (tokenList && !tokenList->getFiles().empty())
file0 = tokenList->getFiles()[0];
setmsg(msg);
2020-07-21 15:59:48 +02:00
std::ostringstream hashWarning;
for (const ErrorPathItem &e: errorPath)
2020-07-21 17:43:12 +02:00
hashWarning << std::hex << (e.first ? e.first->index() : 0) << " ";
2020-07-21 15:59:48 +02:00
hashWarning << mShortMessage;
hash = bugHunting ? calculateWarningHash(tokenList, hashWarning.str()) : 0;
}
2020-05-23 07:16:49 +02:00
ErrorMessage::ErrorMessage(const tinyxml2::XMLElement * const errmsg)
2020-05-03 20:46:29 +02:00
: incomplete(false),
severity(Severity::none),
cwe(0U),
certainty(Certainty::normal)
2016-10-29 12:18:11 +02:00
{
const char * const unknown = "<UNKNOWN>";
const char *attr = errmsg->Attribute("id");
id = attr ? attr : unknown;
attr = errmsg->Attribute("severity");
severity = attr ? Severity::fromString(attr) : Severity::none;
attr = errmsg->Attribute("cwe");
std::istringstream(attr ? attr : "0") >> cwe.id;
2016-10-29 12:18:11 +02:00
attr = errmsg->Attribute("inconclusive");
certainty = (attr && (std::strcmp(attr, "true") == 0)) ? Certainty::inconclusive : Certainty::normal;
attr = errmsg->Attribute("msg");
2018-06-17 07:54:50 +02:00
mShortMessage = attr ? attr : "";
attr = errmsg->Attribute("verbose");
2018-06-17 07:55:47 +02:00
mVerboseMessage = attr ? attr : "";
2020-07-21 11:27:03 +02:00
attr = errmsg->Attribute("hash");
std::istringstream(attr ? attr : "0") >> hash;
2016-10-29 12:18:11 +02:00
for (const tinyxml2::XMLElement *e = errmsg->FirstChildElement(); e; e = e->NextSiblingElement()) {
if (std::strcmp(e->Name(),"location")==0) {
const char *strfile = e->Attribute("file");
const char *strinfo = e->Attribute("info");
const char *strline = e->Attribute("line");
const char *strcolumn = e->Attribute("column");
const char *file = strfile ? strfile : unknown;
const char *info = strinfo ? strinfo : "";
const int line = strline ? std::atoi(strline) : 0;
const int column = strcolumn ? std::atoi(strcolumn) : 0;
callStack.emplace_back(file, info, line, column);
2016-10-29 12:18:11 +02:00
}
}
}
2020-05-23 07:16:49 +02:00
void ErrorMessage::setmsg(const std::string &msg)
{
// If a message ends to a '\n' and contains only a one '\n'
2018-06-17 07:55:47 +02:00
// it will cause the mVerboseMessage to be empty which will show
// as an empty message to the user if --verbose is used.
// Even this doesn't cause problems with messages that have multiple
// lines, none of the error messages should end into it.
assert(!endsWith(msg,'\n'));
2010-12-30 22:07:27 +01:00
// The summary and verbose message are separated by a newline
// If there is no newline then both the summary and verbose messages
// are the given message
const std::string::size_type pos = msg.find('\n');
2018-06-17 07:59:48 +02:00
const std::string symbolName = mSymbolNames.empty() ? std::string() : mSymbolNames.substr(0, mSymbolNames.find('\n'));
2011-10-13 20:53:06 +02:00
if (pos == std::string::npos) {
2018-06-17 07:54:50 +02:00
mShortMessage = replaceStr(msg, "$symbol", symbolName);
2018-06-17 07:55:47 +02:00
mVerboseMessage = replaceStr(msg, "$symbol", symbolName);
} else if (msg.compare(0,8,"$symbol:") == 0) {
2018-06-17 07:59:48 +02:00
mSymbolNames += msg.substr(8, pos-7);
setmsg(msg.substr(pos + 1));
2011-10-13 20:53:06 +02:00
} else {
2018-06-17 07:54:50 +02:00
mShortMessage = replaceStr(msg.substr(0, pos), "$symbol", symbolName);
2018-06-17 07:55:47 +02:00
mVerboseMessage = replaceStr(msg.substr(pos + 1), "$symbol", symbolName);
}
}
2020-05-23 07:16:49 +02:00
Suppressions::ErrorMessage ErrorMessage::toSuppressionsErrorMessage() const
{
Suppressions::ErrorMessage ret;
2020-07-21 11:27:03 +02:00
ret.hash = hash;
ret.errorId = id;
if (!callStack.empty()) {
ret.setFileName(callStack.back().getfile(false));
ret.lineNumber = callStack.back().line;
}
ret.certainty = certainty;
2018-06-17 07:59:48 +02:00
ret.symbolNames = mSymbolNames;
return ret;
}
2020-05-23 07:16:49 +02:00
std::string ErrorMessage::serialize() const
{
2010-12-30 22:07:27 +01:00
// Serialize this message into a simple string
std::ostringstream oss;
oss << id.length() << " " << id;
oss << Severity::toString(severity).length() << " " << Severity::toString(severity);
oss << MathLib::toString(cwe.id).length() << " " << MathLib::toString(cwe.id);
2020-07-21 11:27:03 +02:00
oss << MathLib::toString(hash).length() << " " << MathLib::toString(hash);
oss << file0.size() << " " << file0;
if (certainty == Certainty::inconclusive) {
const std::string text("inconclusive");
oss << text.length() << " " << text;
}
2018-06-17 07:54:50 +02:00
const std::string saneShortMessage = fixInvalidChars(mShortMessage);
2018-06-17 07:55:47 +02:00
const std::string saneVerboseMessage = fixInvalidChars(mVerboseMessage);
oss << saneShortMessage.length() << " " << saneShortMessage;
oss << saneVerboseMessage.length() << " " << saneVerboseMessage;
oss << callStack.size() << " ";
2020-05-23 07:16:49 +02:00
for (std::list<ErrorMessage::FileLocation>::const_iterator loc = callStack.begin(); loc != callStack.end(); ++loc) {
std::ostringstream smallStream;
smallStream << (*loc).line << '\t' << (*loc).column << '\t' << (*loc).getfile(false) << '\t' << loc->getOrigFile(false) << '\t' << loc->getinfo();
oss << smallStream.str().length() << " " << smallStream.str();
}
return oss.str();
}
2020-05-23 07:16:49 +02:00
bool ErrorMessage::deserialize(const std::string &data)
{
certainty = Certainty::normal;
callStack.clear();
std::istringstream iss(data);
std::array<std::string, 7> results;
std::size_t elem = 0;
while (iss.good() && elem < 7) {
unsigned int len = 0;
if (!(iss >> len))
return false;
iss.get();
std::string temp;
2011-10-13 20:53:06 +02:00
for (unsigned int i = 0; i < len && iss.good(); ++i) {
const char c = static_cast<char>(iss.get());
temp.append(1, c);
}
2011-10-13 20:53:06 +02:00
if (temp == "inconclusive") {
certainty = Certainty::inconclusive;
continue;
}
results[elem++] = temp;
}
if (elem != 7)
throw InternalError(nullptr, "Internal Error: Deserialization of error message failed");
id = results[0];
severity = Severity::fromString(results[1]);
std::istringstream(results[2]) >> cwe.id;
2020-07-21 11:27:03 +02:00
std::istringstream(results[3]) >> hash;
std::istringstream(results[4]) >> file0;
mShortMessage = results[5];
mVerboseMessage = results[6];
unsigned int stackSize = 0;
if (!(iss >> stackSize))
return false;
2011-10-13 20:53:06 +02:00
while (iss.good()) {
unsigned int len = 0;
if (!(iss >> len))
return false;
iss.get();
std::string temp;
2011-10-13 20:53:06 +02:00
for (unsigned int i = 0; i < len && iss.good(); ++i) {
2016-02-27 16:03:50 +01:00
const char c = static_cast<char>(iss.get());
temp.append(1, c);
}
std::vector<std::string> substrings;
for (std::string::size_type pos = 0; pos < temp.size() && substrings.size() < 5; ++pos) {
if (substrings.size() == 4) {
substrings.push_back(temp.substr(pos));
break;
}
const std::string::size_type start = pos;
pos = temp.find("\t", pos);
if (pos == std::string::npos) {
substrings.push_back(temp.substr(start));
break;
}
substrings.push_back(temp.substr(start, pos - start));
}
if (substrings.size() < 4)
throw InternalError(nullptr, "Internal Error: serializing/deserializing of error message failed!");
// (*loc).line << '\t' << (*loc).column << '\t' << (*loc).getfile(false) << '\t' << loc->getOrigFile(false) << '\t' << loc->getinfo();
2020-05-23 07:16:49 +02:00
ErrorMessage::FileLocation loc(substrings[3], MathLib::toLongNumber(substrings[0]), MathLib::toLongNumber(substrings[1]));
loc.setfile(substrings[2]);
if (substrings.size() == 5)
loc.setinfo(substrings[4]);
callStack.push_back(loc);
if (callStack.size() >= stackSize)
break;
}
return true;
}
2020-05-23 07:16:49 +02:00
std::string ErrorMessage::getXMLHeader()
{
2013-08-07 16:30:55 +02:00
tinyxml2::XMLPrinter printer;
2013-08-07 16:30:55 +02:00
// standard xml header
printer.PushDeclaration("xml version=\"1.0\" encoding=\"UTF-8\"");
2010-12-30 22:07:27 +01:00
2013-08-07 16:30:55 +02:00
// header
2014-03-01 11:15:08 +01:00
printer.OpenElement("results", false);
2017-07-29 18:56:22 +02:00
printer.PushAttribute("version", 2);
printer.OpenElement("cppcheck", false);
printer.PushAttribute("version", CppCheck::version());
printer.CloseElement(false);
printer.OpenElement("errors", false);
2010-12-22 10:29:23 +01:00
2013-08-07 16:30:55 +02:00
return std::string(printer.CStr()) + '>';
}
2020-05-23 07:16:49 +02:00
std::string ErrorMessage::getXMLFooter()
{
2017-07-29 18:56:22 +02:00
return " </errors>\n</results>";
}
// There is no utf-8 support around but the strings should at least be safe for to tinyxml2.
// See #5300 "Invalid encoding in XML output" and #6431 "Invalid XML created - Invalid encoding of string literal "
2020-05-23 07:16:49 +02:00
std::string ErrorMessage::fixInvalidChars(const std::string& raw)
{
std::string result;
result.reserve(raw.length());
std::string::const_iterator from=raw.begin();
while (from!=raw.end()) {
if (std::isprint(static_cast<unsigned char>(*from))) {
result.push_back(*from);
} else {
std::ostringstream es;
// straight cast to (unsigned) doesn't work out.
const unsigned uFrom = (unsigned char)*from;
es << '\\' << std::setbase(8) << std::setw(3) << std::setfill('0') << uFrom;
result += es.str();
}
++from;
}
return result;
}
2020-05-23 07:16:49 +02:00
std::string ErrorMessage::toXML() const
{
tinyxml2::XMLPrinter printer(nullptr, false, 2);
2017-07-29 18:56:22 +02:00
printer.OpenElement("error", false);
printer.PushAttribute("id", id.c_str());
printer.PushAttribute("severity", Severity::toString(severity).c_str());
2018-06-17 07:54:50 +02:00
printer.PushAttribute("msg", fixInvalidChars(mShortMessage).c_str());
2018-06-17 07:55:47 +02:00
printer.PushAttribute("verbose", fixInvalidChars(mVerboseMessage).c_str());
if (cwe.id)
printer.PushAttribute("cwe", cwe.id);
2020-07-21 11:27:03 +02:00
if (hash)
printer.PushAttribute("hash", MathLib::toString(hash).c_str());
if (certainty == Certainty::inconclusive)
2017-07-29 18:56:22 +02:00
printer.PushAttribute("inconclusive", "true");
if (!file0.empty())
printer.PushAttribute("file0", file0.c_str());
for (std::list<FileLocation>::const_reverse_iterator it = callStack.rbegin(); it != callStack.rend(); ++it) {
2017-07-29 18:56:22 +02:00
printer.OpenElement("location", false);
printer.PushAttribute("file", (*it).getfile().c_str());
printer.PushAttribute("line", std::max((*it).line,0));
printer.PushAttribute("column", (*it).column);
2017-07-29 18:56:22 +02:00
if (!it->getinfo().empty())
printer.PushAttribute("info", fixInvalidChars(it->getinfo()).c_str());
2014-03-01 11:15:08 +01:00
printer.CloseElement(false);
}
2018-06-17 07:59:48 +02:00
for (std::string::size_type pos = 0; pos < mSymbolNames.size();) {
const std::string::size_type pos2 = mSymbolNames.find('\n', pos);
std::string symbolName;
if (pos2 == std::string::npos) {
2018-06-17 07:59:48 +02:00
symbolName = mSymbolNames.substr(pos);
pos = pos2;
} else {
2018-06-17 07:59:48 +02:00
symbolName = mSymbolNames.substr(pos, pos2-pos);
pos = pos2 + 1;
}
printer.OpenElement("symbol", false);
printer.PushText(symbolName.c_str());
printer.CloseElement(false);
}
2017-07-29 18:56:22 +02:00
printer.CloseElement(false);
return printer.CStr();
}
2020-05-23 07:16:49 +02:00
void ErrorMessage::findAndReplace(std::string &source, const std::string &searchFor, const std::string &replaceWith)
{
std::string::size_type index = 0;
2011-10-13 20:53:06 +02:00
while ((index = source.find(searchFor, index)) != std::string::npos) {
source.replace(index, searchFor.length(), replaceWith);
2015-09-02 16:06:19 +02:00
index += replaceWith.length();
}
}
2018-04-23 09:18:32 +02:00
// TODO: read info from some shared resource instead?
static std::string readCode(const std::string &file, int linenr, int column, const char endl[])
2018-04-23 09:18:32 +02:00
{
std::ifstream fin(file);
std::string line;
while (linenr > 0 && std::getline(fin,line)) {
linenr--;
}
const std::string::size_type endPos = line.find_last_not_of("\r\n\t ");
if (endPos + 1 < line.size())
line.erase(endPos + 1);
std::string::size_type pos = 0;
while ((pos = line.find('\t', pos)) != std::string::npos)
line[pos] = ' ';
return line + endl + std::string((column>0 ? column-1 : column), ' ') + '^';
2018-04-23 09:18:32 +02:00
}
2020-05-23 07:16:49 +02:00
std::string ErrorMessage::toString(bool verbose, const std::string &templateFormat, const std::string &templateLocation) const
{
2010-12-30 22:07:27 +01:00
// Save this ErrorMessage in plain text.
// No template is given
if (templateFormat.empty()) {
std::ostringstream text;
if (!callStack.empty())
2020-05-23 07:16:49 +02:00
text << ErrorLogger::callStackToString(callStack) << ": ";
if (severity != Severity::none) {
text << '(' << Severity::toString(severity);
if (certainty == Certainty::inconclusive)
text << ", inconclusive";
text << ") ";
}
2018-06-17 07:55:47 +02:00
text << (verbose ? mVerboseMessage : mShortMessage);
return text.str();
}
2010-12-30 22:07:27 +01:00
// template is given. Reformat the output according to it
std::string result = templateFormat;
// Support a few special characters to allow to specific formatting, see http://sourceforge.net/apps/phpbb/cppcheck/viewtopic.php?f=4&t=494&sid=21715d362c0dbafd3791da4d9522f814
// Substitution should be done first so messages from cppcheck never get translated.
findAndReplace(result, "\\b", "\b");
findAndReplace(result, "\\n", "\n");
findAndReplace(result, "\\r", "\r");
findAndReplace(result, "\\t", "\t");
findAndReplace(result, "{id}", id);
if (result.find("{inconclusive:") != std::string::npos) {
const std::string::size_type pos1 = result.find("{inconclusive:");
const std::string::size_type pos2 = result.find('}', pos1+1);
const std::string replaceFrom = result.substr(pos1,pos2-pos1+1);
const std::string replaceWith = (certainty == Certainty::inconclusive) ? result.substr(pos1+14, pos2-pos1-14) : std::string();
findAndReplace(result, replaceFrom, replaceWith);
}
findAndReplace(result, "{severity}", Severity::toString(severity));
findAndReplace(result, "{cwe}", MathLib::toString(cwe.id));
2018-06-17 07:55:47 +02:00
findAndReplace(result, "{message}", verbose ? mVerboseMessage : mShortMessage);
2020-05-23 07:16:49 +02:00
findAndReplace(result, "{callstack}", callStack.empty() ? emptyString : ErrorLogger::callStackToString(callStack));
if (!callStack.empty()) {
findAndReplace(result, "{file}", callStack.back().getfile());
findAndReplace(result, "{line}", MathLib::toString(callStack.back().line));
findAndReplace(result, "{column}", MathLib::toString(callStack.back().column));
if (result.find("{code}") != std::string::npos) {
const std::string::size_type pos = result.find('\r');
const char *endl;
if (pos == std::string::npos)
endl = "\n";
else if (pos+1 < result.size() && result[pos+1] == '\n')
endl = "\r\n";
else
endl = "\r";
findAndReplace(result, "{code}", readCode(callStack.back().getOrigFile(), callStack.back().line, callStack.back().column, endl));
}
} else {
findAndReplace(result, "{file}", "nofile");
findAndReplace(result, "{line}", "0");
findAndReplace(result, "{column}", "0");
findAndReplace(result, "{code}", emptyString);
}
if (!templateLocation.empty() && callStack.size() >= 2U) {
for (const FileLocation &fileLocation : callStack) {
std::string text = templateLocation;
findAndReplace(text, "\\b", "\b");
findAndReplace(text, "\\n", "\n");
findAndReplace(text, "\\r", "\r");
findAndReplace(text, "\\t", "\t");
findAndReplace(text, "{file}", fileLocation.getfile());
findAndReplace(text, "{line}", MathLib::toString(fileLocation.line));
findAndReplace(text, "{column}", MathLib::toString(fileLocation.column));
2018-06-17 07:54:50 +02:00
findAndReplace(text, "{info}", fileLocation.getinfo().empty() ? mShortMessage : fileLocation.getinfo());
if (text.find("{code}") != std::string::npos) {
const std::string::size_type pos = text.find('\r');
2018-04-23 09:18:32 +02:00
const char *endl;
if (pos == std::string::npos)
endl = "\n";
else if (pos+1 < text.size() && text[pos+1] == '\n')
2018-04-23 09:18:32 +02:00
endl = "\r\n";
else
endl = "\r";
findAndReplace(text, "{code}", readCode(fileLocation.getOrigFile(), fileLocation.line, fileLocation.column, endl));
2018-04-23 09:18:32 +02:00
}
result += '\n' + text;
}
}
return result;
}
bool ErrorLogger::reportUnmatchedSuppressions(const std::list<Suppressions::Suppression> &unmatched)
{
bool err = false;
// Report unmatched suppressions
2018-09-23 17:02:54 +02:00
for (const Suppressions::Suppression &s : unmatched) {
// don't report "unmatchedSuppression" as unmatched
2018-09-23 17:02:54 +02:00
if (s.errorId == "unmatchedSuppression")
continue;
// check if this unmatched suppression is suppressed
bool suppressed = false;
2018-09-23 17:02:54 +02:00
for (const Suppressions::Suppression &s2 : unmatched) {
if (s2.errorId == "unmatchedSuppression") {
if ((s2.fileName.empty() || s2.fileName == "*" || s2.fileName == s.fileName) &&
2018-09-23 17:02:54 +02:00
(s2.lineNumber == Suppressions::Suppression::NO_LINE || s2.lineNumber == s.lineNumber)) {
suppressed = true;
break;
}
}
}
if (suppressed)
continue;
2020-05-23 07:16:49 +02:00
std::list<ErrorMessage::FileLocation> callStack;
2018-09-23 17:02:54 +02:00
if (!s.fileName.empty())
callStack.emplace_back(s.fileName, s.lineNumber, 0);
reportErr(ErrorMessage(callStack, emptyString, Severity::information, "Unmatched suppression: " + s.errorId, "unmatchedSuppression", Certainty::normal));
err = true;
}
return err;
}
2020-05-23 07:16:49 +02:00
std::string ErrorLogger::callStackToString(const std::list<ErrorMessage::FileLocation> &callStack)
{
std::string str;
2020-05-23 07:16:49 +02:00
for (std::list<ErrorMessage::FileLocation>::const_iterator tok = callStack.begin(); tok != callStack.end(); ++tok) {
str += (tok == callStack.begin() ? "" : " -> ");
str += tok->stringify();
}
return str;
2009-02-01 16:47:36 +01:00
}
2020-05-23 07:16:49 +02:00
ErrorMessage::FileLocation::FileLocation(const Token* tok, const TokenList* tokenList)
: fileIndex(tok->fileIndex()), line(tok->linenr()), column(tok->column()), mOrigFileName(tokenList->getOrigFile(tok)), mFileName(tokenList->file(tok))
{
}
2020-05-23 07:16:49 +02:00
ErrorMessage::FileLocation::FileLocation(const Token* tok, const std::string &info, const TokenList* tokenList)
: fileIndex(tok->fileIndex()), line(tok->linenr()), column(tok->column()), mOrigFileName(tokenList->getOrigFile(tok)), mFileName(tokenList->file(tok)), mInfo(info)
{
}
2020-05-23 07:16:49 +02:00
std::string ErrorMessage::FileLocation::getfile(bool convert) const
{
if (convert)
2018-06-17 07:52:59 +02:00
return Path::toNativeSeparators(mFileName);
return mFileName;
}
2020-05-23 07:16:49 +02:00
std::string ErrorMessage::FileLocation::getOrigFile(bool convert) const
{
if (convert)
return Path::toNativeSeparators(mOrigFileName);
return mOrigFileName;
}
2020-05-23 07:16:49 +02:00
void ErrorMessage::FileLocation::setfile(const std::string &file)
{
2018-06-17 07:52:59 +02:00
mFileName = file;
mFileName = Path::fromNativeSeparators(mFileName);
mFileName = Path::simplifyPath(mFileName);
}
2020-05-23 07:16:49 +02:00
std::string ErrorMessage::FileLocation::stringify() const
{
std::string str;
str += '[';
str += Path::toNativeSeparators(mFileName);
if (line != Suppressions::Suppression::NO_LINE) {
str += ':';
str += std::to_string(line);
}
str += ']';
return str;
}
std::string ErrorLogger::toxml(const std::string &str)
{
std::ostringstream xml;
for (unsigned char c : str) {
switch (c) {
case '<':
xml << "&lt;";
break;
case '>':
xml << "&gt;";
break;
case '&':
xml << "&amp;";
break;
case '\"':
xml << "&quot;";
break;
case '\'':
xml << "&apos;";
break;
case '\0':
xml << "\\0";
break;
default:
2018-03-31 10:27:34 +02:00
if (c >= ' ' && c <= 0x7f)
xml << c;
else
xml << 'x';
break;
}
}
return xml.str();
}
2017-05-16 14:07:23 +02:00
std::string ErrorLogger::plistHeader(const std::string &version, const std::vector<std::string> &files)
{
std::ostringstream ostr;
ostr << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
<< "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\r\n"
<< "<plist version=\"1.0\">\r\n"
<< "<dict>\r\n"
<< " <key>clang_version</key>\r\n"
<< "<string>cppcheck version " << version << "</string>\r\n"
<< " <key>files</key>\r\n"
<< " <array>\r\n";
for (const std::string & file : files)
ostr << " <string>" << ErrorLogger::toxml(file) << "</string>\r\n";
2017-05-16 14:07:23 +02:00
ostr << " </array>\r\n"
<< " <key>diagnostics</key>\r\n"
<< " <array>\r\n";
return ostr.str();
}
2020-05-23 07:16:49 +02:00
static std::string plistLoc(const char indent[], const ErrorMessage::FileLocation &loc)
2017-05-16 14:07:23 +02:00
{
std::ostringstream ostr;
ostr << indent << "<dict>\r\n"
<< indent << ' ' << "<key>line</key><integer>" << loc.line << "</integer>\r\n"
<< indent << ' ' << "<key>col</key><integer>" << loc.column << "</integer>\r\n"
2017-05-17 15:22:51 +02:00
<< indent << ' ' << "<key>file</key><integer>" << loc.fileIndex << "</integer>\r\n"
2017-05-16 14:07:23 +02:00
<< indent << "</dict>\r\n";
return ostr.str();
}
2020-05-23 07:16:49 +02:00
std::string ErrorLogger::plistData(const ErrorMessage &msg)
2017-05-16 14:07:23 +02:00
{
std::ostringstream plist;
plist << " <dict>\r\n"
<< " <key>path</key>\r\n"
<< " <array>\r\n";
2020-05-23 07:16:49 +02:00
std::list<ErrorMessage::FileLocation>::const_iterator prev = msg.callStack.begin();
2017-05-16 14:07:23 +02:00
2020-05-23 07:16:49 +02:00
for (std::list<ErrorMessage::FileLocation>::const_iterator it = msg.callStack.begin(); it != msg.callStack.end(); ++it) {
2017-05-16 14:07:23 +02:00
if (prev != it) {
plist << " <dict>\r\n"
<< " <key>kind</key><string>control</string>\r\n"
<< " <key>edges</key>\r\n"
<< " <array>\r\n"
<< " <dict>\r\n"
<< " <key>start</key>\r\n"
<< " <array>\r\n"
<< plistLoc(" ", *prev)
<< plistLoc(" ", *prev)
<< " </array>\r\n"
<< " <key>end</key>\r\n"
<< " <array>\r\n"
<< plistLoc(" ", *it)
<< plistLoc(" ", *it)
<< " </array>\r\n"
<< " </dict>\r\n"
<< " </array>\r\n"
<< " </dict>\r\n";
prev = it;
}
2020-05-23 07:16:49 +02:00
std::list<ErrorMessage::FileLocation>::const_iterator next = it;
2017-05-16 14:07:23 +02:00
++next;
const std::string message = (it->getinfo().empty() && next == msg.callStack.end() ? msg.shortMessage() : it->getinfo());
2017-05-16 14:07:23 +02:00
plist << " <dict>\r\n"
<< " <key>kind</key><string>event</string>\r\n"
<< " <key>location</key>\r\n"
<< plistLoc(" ", *it)
<< " <key>ranges</key>\r\n"
<< " <array>\r\n"
<< " <array>\r\n"
<< plistLoc(" ", *it)
<< plistLoc(" ", *it)
<< " </array>\r\n"
<< " </array>\r\n"
<< " <key>depth</key><integer>0</integer>\r\n"
<< " <key>extended_message</key>\r\n"
2017-05-17 15:22:51 +02:00
<< " <string>" << ErrorLogger::toxml(message) << "</string>\r\n"
<< " <key>message</key>\r\n"
2017-05-17 15:22:51 +02:00
<< " <string>" << ErrorLogger::toxml(message) << "</string>\r\n"
2017-05-16 14:07:23 +02:00
<< " </dict>\r\n";
}
plist << " </array>\r\n"
<< " <key>description</key><string>" << ErrorLogger::toxml(msg.shortMessage()) << "</string>\r\n"
<< " <key>category</key><string>" << Severity::toString(msg.severity) << "</string>\r\n"
2017-05-16 14:07:23 +02:00
<< " <key>type</key><string>" << ErrorLogger::toxml(msg.shortMessage()) << "</string>\r\n"
<< " <key>check_name</key><string>" << msg.id << "</string>\r\n"
2017-05-16 14:07:23 +02:00
<< " <!-- This hash is experimental and going to change! -->\r\n"
<< " <key>issue_hash_content_of_line_in_context</key><string>" << 0 << "</string>\r\n"
<< " <key>issue_context_kind</key><string></string>\r\n"
<< " <key>issue_context</key><string></string>\r\n"
<< " <key>issue_hash_function_offset</key><string></string>\r\n"
<< " <key>location</key>\r\n"
<< plistLoc(" ", msg.callStack.back())
2017-05-16 14:07:23 +02:00
<< " </dict>\r\n";
return plist.str();
}
std::string replaceStr(std::string s, const std::string &from, const std::string &to)
{
std::string::size_type pos1 = 0;
while (pos1 < s.size()) {
pos1 = s.find(from, pos1);
if (pos1 == std::string::npos)
return s;
if (pos1 > 0 && (s[pos1-1] == '_' || std::isalnum(s[pos1-1]))) {
pos1++;
continue;
}
const std::string::size_type pos2 = pos1 + from.size();
if (pos2 >= s.size())
return s.substr(0,pos1) + to;
if (s[pos2] == '_' || std::isalnum(s[pos2])) {
pos1++;
continue;
}
s = s.substr(0,pos1) + to + s.substr(pos2);
pos1 += to.size();
}
return s;
}