From dd666b7c1b388a72162716c9a97b638d9c708adb Mon Sep 17 00:00:00 2001 From: Reijo Tomperi Date: Tue, 16 Aug 2011 22:58:27 +0300 Subject: [PATCH] Add command line option: --debug-fp ... If used, cppcheck will print out the code generating error into output stream. This is ment to be used for debugging false positive errors in Cppcheck. Current implementation tries two alternatives. Without all headers or with all headers and prints out the option with less code. In future versions this could try with individual headers or group of header files. --- cli/cmdlineparser.cpp | 5 +++ lib/cppcheck.cpp | 89 ++++++++++++++++++++++++++++++++++++++++++- lib/cppcheck.h | 13 +++++++ lib/settings.cpp | 1 + lib/settings.h | 3 ++ 5 files changed, 110 insertions(+), 1 deletion(-) diff --git a/cli/cmdlineparser.cpp b/cli/cmdlineparser.cpp index 1cc7188a6..9966a6832 100644 --- a/cli/cmdlineparser.cpp +++ b/cli/cmdlineparser.cpp @@ -83,6 +83,7 @@ bool CmdLineParser::ParseFromArgs(int argc, const char* const argv[]) _exitAfterPrint = true; return true; } + // Flag used for various purposes during debugging else if (strcmp(argv[i], "--debug") == 0) _settings->debug = _settings->debugwarnings = true; @@ -91,6 +92,10 @@ bool CmdLineParser::ParseFromArgs(int argc, const char* const argv[]) else if (strcmp(argv[i], "--debug-warnings") == 0) _settings->debugwarnings = true; + // Print out code that triggers false positive + else if (strcmp(argv[i], "--debug-fp") == 0) + _settings->debugFalsePositive = true; + // Enable all checks - will be removed in future else if (strcmp(argv[i], "-a") == 0 || strcmp(argv[i], "--all") == 0) { diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index 774c3e32e..1e6d1e9f3 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -82,6 +82,73 @@ unsigned int CppCheck::check(const std::string &path, const std::string &content return retval; } +std::string CppCheck::replaceAll(std::string code, const std::string &from, const std::string &to) +{ + size_t pos = 0; + while ((pos = code.find(from, pos)) != std::string::npos) + { + code.replace(pos, from.length(), to); + pos += to.length(); + } + + return code; +} + +bool CppCheck::findError(std::string code, const char FileName[]) +{ + // First make sure that error occurs with the original code + checkFile(code, FileName); + if (_errorList.empty()) + { + // Error does not occur with this code + return false; + } + + std::string previousCode = code; + std::string error = _errorList.front(); + for (;;) + { + + // Try to remove included files from the source + size_t found=previousCode.rfind("\n#endfile"); + if (found == std::string::npos) + { + // No modifications can be done to the code + } + else + { + // Modify code and re-check it to see if error + // is still there. + code = previousCode.substr(found+9); + _errorList.clear(); + checkFile(code, FileName); + } + + if (_errorList.empty()) + { + // Latest code didn't fail anymore. Fall back + // to previous code + code = previousCode; + } + else + { + error = _errorList.front(); + } + + // Add '\n' so that "\n#file" on first line would be found + code = "// " + error + "\n" + code; + code = replaceAll(code, "\n#file", "\n// #file"); + code = replaceAll(code, "\n#endfile", "\n// #endfile"); + + // We have reduced the code as much as we can. Print out + // the code and quit. + _errorLogger.reportOut(code); + break; + } + + return true; +} + unsigned int CppCheck::processFile() { exitcode = 0; @@ -186,7 +253,18 @@ unsigned int CppCheck::processFile() if (!appendCode.empty()) Preprocessor::preprocessWhitespaces(appendCode); - checkFile(codeWithoutCfg + appendCode, _filename.c_str()); + if (_settings.debugFalsePositive) + { + if (findError(codeWithoutCfg + appendCode, _filename.c_str())) + { + return exitcode; + } + } + else + { + checkFile(codeWithoutCfg + appendCode, _filename.c_str()); + } + ++checkCount; } } @@ -204,6 +282,8 @@ unsigned int CppCheck::processFile() return exitcode; } + + void CppCheck::checkFunctionUsage() { // This generates false positives - especially for libraries @@ -409,6 +489,13 @@ void CppCheck::reportErr(const ErrorLogger::ErrorMessage &msg) if (errmsg.empty()) return; + if (_settings.debugFalsePositive) + { + // Don't print out error + _errorList.push_back(errmsg); + return; + } + // Alert only about unique errors if (std::find(_errorList.begin(), _errorList.end(), errmsg) != _errorList.end()) return; diff --git a/lib/cppcheck.h b/lib/cppcheck.h index 301329a95..73a5ccc05 100644 --- a/lib/cppcheck.h +++ b/lib/cppcheck.h @@ -158,6 +158,19 @@ private: */ virtual void reportOut(const std::string &outmsg); + /** + * @brief Check given code. If error is found, return true + * and print out source of the file. Try to reduce the code + * while still showing the error. + */ + bool findError(std::string code, const char FileName[]); + + /** + * @brief Replace "from" strings with "to" strings in "code" + * and return it. + */ + std::string replaceAll(std::string code, const std::string &from, const std::string &to); + unsigned int exitcode; std::list _errorList; std::ostringstream _errout; diff --git a/lib/settings.cpp b/lib/settings.cpp index 43c630c07..efc6a37ad 100644 --- a/lib/settings.cpp +++ b/lib/settings.cpp @@ -31,6 +31,7 @@ Settings::Settings() { debug = debugwarnings = false; + debugFalsePositive = false; _errorsOnly = false; _inlineSuppressions = false; _verbose = false; diff --git a/lib/settings.h b/lib/settings.h index 80752171f..afe4b306f 100644 --- a/lib/settings.h +++ b/lib/settings.h @@ -55,6 +55,9 @@ public: /** @brief Is --debug-warnings given? */ bool debugwarnings; + /** @brief Is --debug-fp given? */ + bool debugFalsePositive; + /** @brief Inconclusive checks */ bool inconclusive;