diff --git a/addons/cert.py b/addons/cert.py index 6dbe1e233..4d8517a2f 100755 --- a/addons/cert.py +++ b/addons/cert.py @@ -16,13 +16,17 @@ import re VERIFY = ('-verify' in sys.argv) VERIFY_EXPECTED = [] VERIFY_ACTUAL = [] +CLI = False def reportError(token, severity, msg, id): if VERIFY: VERIFY_ACTUAL.append(str(token.linenr) + ':' + id) else: - sys.stderr.write( - '[' + token.file + ':' + str(token.linenr) + '] (' + severity + '): ' + msg + ' [' + id + ']\n') + msg = '[' + token.file + ':' + str(token.linenr) + ']: (' + severity + ') ' + msg + ' [' + id + ']' + if CLI: + print(msg) + else: + sys.stderr.write(msg + '\n') def simpleMatch(token, pattern): for p in pattern.split(' '): @@ -239,6 +243,9 @@ for arg in sys.argv[1:]: if arg == '-verify': VERIFY = True continue + if arg == '--cli': + CLI = True + continue print('Checking ' + arg + '...') data = cppcheckdata.parsedump(arg) diff --git a/addons/misra.py b/addons/misra.py index b18254aa4..f4fb7da69 100755 --- a/addons/misra.py +++ b/addons/misra.py @@ -35,6 +35,7 @@ typeBits = { VERIFY = False QUIET = False SHOW_SUMMARY = True +CLI = False # Executed by Cppcheck binary? def printStatus(*args, **kwargs): if not QUIET: @@ -1964,8 +1965,11 @@ class MisraChecker: errorId = id, suppressions = self.dumpfileSuppressions) if formattedMsg: - sys.stderr.write(formattedMsg) - sys.stderr.write('\n') + if CLI: + print(formattedMsg) + else: + sys.stderr.write(formattedMsg) + sys.stderr.write('\n') if not severity in self.violations: self.violations[severity] = [] self.violations[severity].append(id) @@ -2193,6 +2197,7 @@ parser.add_argument("-generate-table", help=argparse.SUPPRESS, action="store_tru parser.add_argument("dumpfile", nargs='*', help="Path of dump file from cppcheck") parser.add_argument("--show-suppressed-rules", help="Print rule suppression list", action="store_true") parser.add_argument("-P", "--file-prefix", type=str, help="Prefix to strip when matching suppression file rules") +parser.add_argument("--cli", help="Addon is executed from Cppcheck", action="store_true") args = parser.parse_args() checker = MisraChecker() @@ -2215,6 +2220,10 @@ else: if args.file_prefix: checker.setFilePrefix(args.file_prefix) + if args.cli: + CLI = True + QUIET = True + SHOW_SUMMARY = False if args.quiet: QUIET = True if args.no_summary: diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index 9956f1017..247d9cf38 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -40,6 +40,8 @@ #include #include #include +#include +#include // <- TEMPORARY #ifdef HAVE_RULES #define PCRE_STATIC @@ -216,9 +218,10 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string // write dump file xml prolog std::ofstream fdump; - if (mSettings.dump) { - const std::string dumpfile(mSettings.dumpFile.empty() ? (filename + ".dump") : mSettings.dumpFile); - fdump.open(dumpfile); + std::string dumpFile; + if (mSettings.dump || !mSettings.addons.empty()) { + dumpFile = (mSettings.dumpFile.empty()) ? (filename + ".dump") : mSettings.dumpFile; + fdump.open(dumpFile); if (fdump.is_open()) { fdump << "" << std::endl; fdump << "" << std::endl; @@ -247,7 +250,7 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string // Parse comments and then remove them preprocessor.inlineSuppressions(tokens1); - if (mSettings.dump && fdump.is_open()) { + if ((mSettings.dump || !mSettings.addons.empty()) && fdump.is_open()) { mSettings.nomsg.dump(fdump); } tokens1.removeComments(); @@ -409,7 +412,7 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string continue; // dump xml if --dump - if (mSettings.dump && fdump.is_open()) { + if ((mSettings.dump || !mSettings.addons.empty()) && fdump.is_open()) { fdump << "" << std::endl; preprocessor.dump(fdump); mTokenizer.dump(fdump); @@ -504,9 +507,71 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string } // dumped all configs, close root element now - if (mSettings.dump && fdump.is_open()) + if ((mSettings.dump || !mSettings.addons.empty()) && fdump.is_open()) fdump << "" << std::endl; + if (!mSettings.addons.empty()) { + fdump.close(); + + for (const std::string &addon : mSettings.addons) { + const std::string &results = executeAddon(addon, dumpFile); + for (std::string::size_type pos = 0; pos < results.size();) { + const std::string::size_type pos2 = results.find("\n", pos); + if (pos2 == std::string::npos) + break; + + const std::string::size_type pos1 = pos; + pos = pos2 + 1; + + if (pos1 + 5 > pos2) + continue; + if (results[pos1] != '[') + continue; + if (results[pos2-1] != ']') + continue; + + const std::string line = results.substr(pos1, pos2-pos1); + + const std::string::size_type loc1 = 1; + const std::string::size_type loc2 = line.find(':'); + const std::string::size_type loc3 = line.find("]: ("); + if (loc2 == std::string::npos || loc3 == std::string::npos) + continue; + if (!(loc1 < loc2 && loc2 < loc3)) + continue; + + const std::string::size_type sev1 = loc3 + 4; + const std::string::size_type sev2 = line.find(")", sev1); + if (sev2 == std::string::npos) + continue; + + const std::string::size_type id1 = line.rfind("[" + addon + "-"); + if (id1 == std::string::npos || id1 < loc3) + continue; + + ErrorLogger::ErrorMessage errmsg; + + const std::string filename = line.substr(loc1, loc2-loc1); + const int lineNumber = std::atoi(line.c_str() + loc2 + 1); + errmsg._callStack.emplace_back(ErrorLogger::ErrorMessage::FileLocation(filename, lineNumber)); + + errmsg._id = line.substr(id1+1, line.size()-id1-2); + std::string text = line.substr(loc3 + 11, id1 - loc3 - 11); + if (text[0] == ' ') + text = text.substr(1); + if (endsWith(text, " ", 1)) + text = text.erase(text.size() - 1); + errmsg.setmsg(text); + errmsg._severity = Severity::fromString(line.substr(sev1, sev2-sev1)); + if (errmsg._severity == Severity::SeverityType::none) + continue; + errmsg.file0 = filename; + + reportErr(errmsg); + } + } + } + } catch (const std::runtime_error &e) { internalError(filename, e.what()); } catch (const std::bad_alloc &e) { @@ -879,6 +944,27 @@ void CppCheck::executeRules(const std::string &tokenlist, const Tokenizer &token #endif } +std::string CppCheck::executeAddon(const std::string &addon, const std::string &dumpFile) +{ + const std::string addonFile = "addons/" + addon + ".py"; + +#ifdef _WIN32 + return ""; +#else + const std::string cmd = "python " + addonFile + " --cli " + dumpFile; + + char buffer[1024]; + std::string result; + std::unique_ptr pipe(popen(cmd.c_str(), "r"), pclose); + if (!pipe) + return ""; + while (fgets(buffer, sizeof(buffer), pipe.get()) != nullptr) { + result += buffer; + } + return result; +#endif +} + Settings &CppCheck::settings() { return mSettings; diff --git a/lib/cppcheck.h b/lib/cppcheck.h index 0e27aa61d..613bb3dc2 100644 --- a/lib/cppcheck.h +++ b/lib/cppcheck.h @@ -179,6 +179,12 @@ private: */ void executeRules(const std::string &tokenlist, const Tokenizer &tokenizer); + /** + * @brief Execute a given addon + * @return results in std::string + */ + std::string executeAddon(const std::string &addon, const std::string &dumpFile); + /** * @brief Errors and warnings are directed here. * diff --git a/lib/importproject.cpp b/lib/importproject.cpp index 18aa29dc3..88cf5b9c7 100644 --- a/lib/importproject.cpp +++ b/lib/importproject.cpp @@ -1017,7 +1017,7 @@ bool ImportProject::importCppcheckGuiProject(std::istream &istr, Settings *setti else if (strcmp(node->Name(), AnalyzeAllVsConfigsElementName) == 0) ; // FIXME: Write some warning else if (strcmp(node->Name(), AddonsElementName) == 0) - node->Attribute(AddonElementName); // FIXME: Handle addons + temp.addons = readXmlStringList(node, AddonElementName, nullptr); else if (strcmp(node->Name(), TagsElementName) == 0) node->Attribute(TagElementName); // FIXME: Write some warning else if (strcmp(node->Name(), ToolsElementName) == 0) @@ -1030,6 +1030,7 @@ bool ImportProject::importCppcheckGuiProject(std::istream &istr, Settings *setti settings->includePaths = temp.includePaths; settings->userDefines = temp.userDefines; settings->userUndefs = temp.userUndefs; + settings->addons = temp.addons; for (const std::string &path : paths) guiProject.pathNames.push_back(path); for (const std::string &supp : suppressions) diff --git a/lib/settings.h b/lib/settings.h index fe9f4b3d0..429c765ad 100644 --- a/lib/settings.h +++ b/lib/settings.h @@ -277,6 +277,8 @@ public: */ std::list rules; + std::list addons; + /** Is the 'configuration checking' wanted? */ bool checkConfiguration;