CLI: Added more fields for --template and added a new --template-location. The gcc predefined template now matches latest gcc better.

This commit is contained in:
Daniel Marjamäki 2018-04-23 12:21:18 +02:00
parent b44a40801b
commit f058d9ad08
8 changed files with 139 additions and 84 deletions

View File

@ -538,23 +538,42 @@ bool CmdLineParser::ParseFromArgs(int argc, const char* const argv[])
// Output formatter
else if (std::strcmp(argv[i], "--template") == 0 ||
std::strncmp(argv[i], "--template=", 11) == 0) {
// "--template path/"
// "--template format"
if (argv[i][10] == '=')
_settings->outputFormat = argv[i] + 11;
_settings->templateFormat = argv[i] + 11;
else if ((i+1) < argc && argv[i+1][0] != '-') {
++i;
_settings->outputFormat = argv[i];
_settings->templateFormat = argv[i];
} else {
PrintMessage("cppcheck: argument to '--template' is missing.");
return false;
}
if (_settings->outputFormat == "gcc")
_settings->outputFormat = "{file}:{line}: {severity}: {message}";
else if (_settings->outputFormat == "vs")
_settings->outputFormat = "{file}({line}): {severity}: {message}";
else if (_settings->outputFormat == "edit")
_settings->outputFormat = "{file} +{line}: {severity}: {message}";
if (_settings->templateFormat == "gcc") {
//_settings->templateFormat = "{file}:{line}: {severity}: {message}";
_settings->templateFormat = "{file}:{line}:{column}: warning: {message} [{id}]\\n{code}";
_settings->templateLocation = "{file}:{line}:{column}: note: {info}\\n{code}";
} else if (_settings->templateFormat == "daca2") {
_settings->templateFormat = "{file}:{line}:{column}: {inconclusive:inconclusive }{severity}: {message} [{id}]\\n{code}";
_settings->templateLocation = "{file}:{line}:{column}: note: {info}\\n{code}";
} else if (_settings->templateFormat == "vs")
_settings->templateFormat = "{file}({line}): {severity}: {message}";
else if (_settings->templateFormat == "edit")
_settings->templateFormat = "{file} +{line}: {severity}: {message}";
}
else if (std::strcmp(argv[i], "--template-location") == 0 ||
std::strncmp(argv[i], "--template-location=", 20) == 0) {
// "--template-location format"
if (argv[i][19] == '=')
_settings->templateLocation = argv[i] + 20;
else if ((i+1) < argc && argv[i+1][0] != '-') {
++i;
_settings->templateLocation = argv[i];
} else {
PrintMessage("cppcheck: argument to '--template' is missing.");
return false;
}
}
// Checking threads
@ -1008,11 +1027,40 @@ void CmdLineParser::PrintHelp()
" --suppressions-list=<file>\n"
" Suppress warnings listed in the file. Each suppression\n"
" is in the same format as <spec> above.\n"
" --template='<text>' Format the error messages. E.g.\n"
" --template='<text>' Format the error messages. Available fields:\n"
" {file} file name\n"
" {line} line number\n"
" {column} column number\n"
" {callstack} show a callstack. Example:\n"
" [file.c:1] -> [file.c:100]\n"
" {inconlusive:text} if warning is inconclusive, text\n"
" is written\n"
" {severity} severity\n"
" {message} warning message\n"
" {id} warning id\n"
" {code} show the real code\n"
" \\t insert tab\n"
" \\n insert newline\n"
" \\r insert carriage return\n"
" Example formats:\n"
" '{file}:{line},{severity},{id},{message}' or\n"
" '{file}({line}):({severity}) {message}' or\n"
" '{callstack} {message}'\n"
" Pre-defined templates: gcc, vs, edit.\n"
" --template-location='<text>'\n"
" Format error message location. If this is not provided\n"
" then no extra location info is shown.\n"
" Available fields:\n"
" {file} file name\n"
" {line} line number\n"
" {column} column number\n"
" {info} location info\n"
" {code} show the real code\n"
" \\t insert tab\n"
" \\n insert newline\n"
" \\r insert carriage return\n"
" Example format (gcc-like):\n"
" '{file}:{line}:{column}: note: {info}\\n{code}'\n"
" -v, --verbose Output more detailed error information.\n"
" --version Print out version number.\n"
" --xml Write results in xml format to error stream (stderr).\n"

View File

@ -1040,7 +1040,7 @@ void CppCheckExecutor::reportErr(const ErrorLogger::ErrorMessage &msg)
} else if (_settings->xml) {
reportErr(msg.toXML());
} else {
reportErr(msg.toString(_settings->verbose, _settings->outputFormat));
reportErr(msg.toString(_settings->verbose, _settings->templateFormat, _settings->templateLocation));
}
}

View File

@ -163,7 +163,7 @@ protected:
ErrorPath errorPath;
if (!value) {
errorPath.emplace_back(errtok,bug);
} else if (_settings->verbose || _settings->xml || _settings->outputFormat == "daca2") {
} else if (_settings->verbose || _settings->xml || !_settings->templateLocation.empty()) {
errorPath = value->errorPath;
errorPath.emplace_back(errtok,bug);
} else {

View File

@ -93,7 +93,7 @@ void CheckBufferOverrun::arrayIndexOutOfBoundsError(const Token *tok, const Arra
}
std::list<ErrorPathItem> errorPath;
if (_settings->xml || _settings->outputFormat == "daca2") {
if (_settings->xml || !_settings->templateLocation.empty()) {
for (std::size_t i = 0; i < index.size(); ++i) {
const ErrorPath &e = getErrorPath(tok, &index[i], "");
for (ErrorPath::const_iterator it = e.begin(); it != e.end(); ++it) {

View File

@ -444,15 +444,15 @@ static std::string readCode(const std::string &file, unsigned int linenr, unsign
const std::string::size_type endPos = line.find_last_not_of("\r\n\t ");
if (endPos + 1 < line.size())
line.erase(endPos + 1);
return line + endl + std::string(column,' ') + '^';
return line + endl + std::string((column>0 ? column-1 : column), ' ') + '^';
}
std::string ErrorLogger::ErrorMessage::toString(bool verbose, const std::string &outputFormat) const
std::string ErrorLogger::ErrorMessage::toString(bool verbose, const std::string &templateFormat, const std::string &templateLocation) const
{
// Save this ErrorMessage in plain text.
// No template is given
if (outputFormat.empty()) {
if (templateFormat.empty()) {
std::ostringstream text;
if (!_callStack.empty())
text << callStackToString(_callStack) << ": ";
@ -466,76 +466,77 @@ std::string ErrorLogger::ErrorMessage::toString(bool verbose, const std::string
return text.str();
}
else if (outputFormat == "daca2") {
// This is a clang-like output format for daca2
std::ostringstream text;
if (_callStack.empty()) {
text << "nofile:0:0: ";
} else {
const ErrorLogger::ErrorMessage::FileLocation &loc = _callStack.back();
text << loc.getfile() << ':' << loc.line << ':' << loc.col << ": ";
// 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 = result.substr(pos1+14, pos2-pos1-14);
findAndReplace(result, replaceFrom, replaceWith);
}
findAndReplace(result, "{severity}", Severity::toString(_severity));
findAndReplace(result, "{message}", verbose ? _verboseMessage : _shortMessage);
findAndReplace(result, "{callstack}", _callStack.empty() ? emptyString : 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().col));
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().getfile(), _callStack.back().line, _callStack.back().col, endl));
}
if (_inconclusive)
text << "inconclusive ";
text << Severity::toString(_severity) << ": ";
text << (verbose ? _verboseMessage : _shortMessage)
<< " [" << _id << ']';
if (_callStack.size() <= 1U)
return text.str();
for (std::list<FileLocation>::const_iterator loc = _callStack.begin(); loc != _callStack.end(); ++loc)
text << std::endl
<< loc->getfile()
<< ':'
<< loc->line
<< ':'
<< loc->col
<< ": note: "
<< (loc->getinfo().empty() ? _shortMessage : loc->getinfo());
return text.str();
} else {
findAndReplace(result, "{file}", "nofile");
findAndReplace(result, "{line}", "0");
findAndReplace(result, "{column}", "0");
findAndReplace(result, "{code}", emptyString);
}
// template is given. Reformat the output according to it
else {
std::string result = outputFormat;
// 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");
if (!templateLocation.empty() && _callStack.size() >= 2U) {
for (const FileLocation &fileLocation : _callStack) {
std::string text = templateLocation;
findAndReplace(result, "{id}", _id);
findAndReplace(result, "{severity}", Severity::toString(_severity));
findAndReplace(result, "{message}", verbose ? _verboseMessage : _shortMessage);
findAndReplace(result, "{callstack}", _callStack.empty() ? emptyString : 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().col));
if (result.find("{code}") != std::string::npos) {
const std::string::size_type pos = result.find("\r");
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.col));
findAndReplace(text, "{info}", fileLocation.getinfo().empty() ? _shortMessage : fileLocation.getinfo());
if (text.find("{code}") != std::string::npos) {
const std::string::size_type pos = text.find("\r");
const char *endl;
if (pos == std::string::npos)
endl = "\n";
else if (pos+1 < result.size() && result[pos+1] == '\n')
else if (pos+1 < text.size() && text[pos+1] == '\n')
endl = "\r\n";
else
endl = "\r";
findAndReplace(result, "{code}", readCode(_callStack.back().getfile(), _callStack.back().line, _callStack.back().col, endl));
findAndReplace(text, "{code}", readCode(fileLocation.getfile(), fileLocation.line, fileLocation.col, endl));
}
} else {
findAndReplace(result, "{file}", emptyString);
findAndReplace(result, "{line}", emptyString);
findAndReplace(result, "{column}", emptyString);
findAndReplace(result, "{code}", emptyString);
result += '\n' + text;
}
return result;
}
return result;
}
void ErrorLogger::reportUnmatchedSuppressions(const std::list<Suppressions::Suppression> &unmatched)

View File

@ -256,11 +256,13 @@ public:
/**
* Format the error message into a string.
* @param verbose use verbose message
* @param outputFormat Empty string to use default output format
* @param templateFormat Empty string to use default output format
* or template to be used. E.g. "{file}:{line},{severity},{id},{message}"
* @param templateLocation Format Empty string to use default output format
* or template to be used. E.g. "{file}:{line},{info}"
* @return formatted string
*/
std::string toString(bool verbose, const std::string &outputFormat = emptyString) const;
std::string toString(bool verbose, const std::string &templateFormat = emptyString, const std::string &templateLocation = emptyString) const;
std::string serialize() const;
bool deserialize(const std::string &data);

View File

@ -158,7 +158,11 @@ public:
/** @brief The output format in which the errors are printed in text mode,
e.g. "{severity} {file}:{line} {message} {id}" */
std::string outputFormat;
std::string templateFormat;
/** @brief The output format in which the error locations are printed in
* text mode, e.g. "{file}:{line} {info}" */
std::string templateLocation;
/** @brief show timing information (--showtime=file|summary|top5) */
SHOWTIME_MODES showtime;

View File

@ -802,33 +802,33 @@ private:
void templates() {
REDIRECT;
const char *argv[] = {"cppcheck", "--template", "{file}:{line},{severity},{id},{message}", "file.cpp"};
settings.outputFormat.clear();
settings.templateFormat.clear();
ASSERT(defParser.ParseFromArgs(4, argv));
ASSERT_EQUALS("{file}:{line},{severity},{id},{message}", settings.outputFormat);
ASSERT_EQUALS("{file}:{line},{severity},{id},{message}", settings.templateFormat);
}
void templatesGcc() {
REDIRECT;
const char *argv[] = {"cppcheck", "--template", "gcc", "file.cpp"};
settings.outputFormat.clear();
settings.templateFormat.clear();
ASSERT(defParser.ParseFromArgs(4, argv));
ASSERT_EQUALS("{file}:{line}: {severity}: {message}", settings.outputFormat);
ASSERT_EQUALS("{file}:{line}:{column}: warning: {message} [{id}]\\n{code}", settings.templateFormat);
}
void templatesVs() {
REDIRECT;
const char *argv[] = {"cppcheck", "--template", "vs", "file.cpp"};
settings.outputFormat.clear();
settings.templateFormat.clear();
ASSERT(defParser.ParseFromArgs(4, argv));
ASSERT_EQUALS("{file}({line}): {severity}: {message}", settings.outputFormat);
ASSERT_EQUALS("{file}({line}): {severity}: {message}", settings.templateFormat);
}
void templatesEdit() {
REDIRECT;
const char *argv[] = {"cppcheck", "--template", "edit", "file.cpp"};
settings.outputFormat.clear();
settings.templateFormat.clear();
ASSERT(defParser.ParseFromArgs(4, argv));
ASSERT_EQUALS("{file} +{line}: {severity}: {message}", settings.outputFormat);
ASSERT_EQUALS("{file} +{line}: {severity}: {message}", settings.templateFormat);
}
void xml() {