diff --git a/cli/cmdlineparser.cpp b/cli/cmdlineparser.cpp index 1171528af..58c56055f 100644 --- a/cli/cmdlineparser.cpp +++ b/cli/cmdlineparser.cpp @@ -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=\n" " Suppress warnings listed in the file. Each suppression\n" " is in the same format as above.\n" - " --template='' Format the error messages. E.g.\n" + " --template='' 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=''\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" diff --git a/cli/cppcheckexecutor.cpp b/cli/cppcheckexecutor.cpp index c92dc6cbb..68e6c36e6 100644 --- a/cli/cppcheckexecutor.cpp +++ b/cli/cppcheckexecutor.cpp @@ -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)); } } diff --git a/lib/check.h b/lib/check.h index c75429f55..8aa5c2026 100644 --- a/lib/check.h +++ b/lib/check.h @@ -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 { diff --git a/lib/checkbufferoverrun.cpp b/lib/checkbufferoverrun.cpp index 4deed3817..daa2da5d0 100644 --- a/lib/checkbufferoverrun.cpp +++ b/lib/checkbufferoverrun.cpp @@ -93,7 +93,7 @@ void CheckBufferOverrun::arrayIndexOutOfBoundsError(const Token *tok, const Arra } std::list 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) { diff --git a/lib/errorlogger.cpp b/lib/errorlogger.cpp index 21cb899e6..bde69d76e 100644 --- a/lib/errorlogger.cpp +++ b/lib/errorlogger.cpp @@ -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::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 &unmatched) diff --git a/lib/errorlogger.h b/lib/errorlogger.h index 9851e0750..175b62039 100644 --- a/lib/errorlogger.h +++ b/lib/errorlogger.h @@ -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); diff --git a/lib/settings.h b/lib/settings.h index ecbc01f43..200a89762 100644 --- a/lib/settings.h +++ b/lib/settings.h @@ -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; diff --git a/test/testcmdlineparser.cpp b/test/testcmdlineparser.cpp index 77633fe1e..35b814e06 100644 --- a/test/testcmdlineparser.cpp +++ b/test/testcmdlineparser.cpp @@ -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() {