From d82da987e517af0960b9470b6be7749334e15ed2 Mon Sep 17 00:00:00 2001 From: fuzzelhjb Date: Thu, 30 Jan 2020 07:14:17 +0100 Subject: [PATCH] Support clang tidy (#2486) --- cli/cppcheckexecutor.cpp | 2 ++ lib/cppcheck.cpp | 77 ++++++++++++++++++++++++++++++++++++++++ lib/cppcheck.h | 3 ++ lib/importproject.cpp | 11 ++++-- lib/importproject.h | 1 + lib/settings.cpp | 1 + lib/settings.h | 3 ++ 7 files changed, 95 insertions(+), 3 deletions(-) diff --git a/cli/cppcheckexecutor.cpp b/cli/cppcheckexecutor.cpp index 663cc78ef..17c35d869 100644 --- a/cli/cppcheckexecutor.cpp +++ b/cli/cppcheckexecutor.cpp @@ -925,6 +925,8 @@ int CppCheckExecutor::check_internal(CppCheck& cppcheck, int /*argc*/, const cha ++c; if (!settings.quiet) reportStatus(c, settings.project.fileSettings.size(), c, settings.project.fileSettings.size()); + if(settings.clangTidy) + cppcheck.analyseClangTidy(fs); } } diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index e409a8fdf..12a9ef7f5 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -1352,6 +1352,83 @@ void CppCheck::getErrorMessages() Preprocessor::getErrorMessages(this, &s); } +void CppCheck::analyseClangTidy(const ImportProject::FileSettings &fileSettings ) +{ + std::string allIncludes = ""; + std::string allDefines = "-D"+fileSettings.defines; + for (const std::string &inc : fileSettings.includePaths) { + allIncludes = allIncludes + "-I\"" + inc + "\" "; + } + + std::string::size_type pos = 0u; + while ((pos = allDefines.find(";", pos)) != std::string::npos) + { + allDefines.replace(pos, 1, " -D"); + pos += 3; + } + + const std::string cmd = "clang-tidy -quiet -checks=*,-clang-analyzer-*,-llvm* \"" + fileSettings.filename + "\" -- " + allIncludes + allDefines; + std::pair result = executeCommand(cmd); + if (!result.first) { + std::cerr << "Failed to execute '" + cmd + "'" << std::endl; + return; + } + + // parse output and create error messages + std::istringstream istr(result.second); + std::string line; + + if (!mSettings.buildDir.empty()) + { + const std::string analyzerInfoFile = AnalyzerInformation::getAnalyzerInfoFile(mSettings.buildDir, fileSettings.filename, ""); + std::ofstream fcmd(analyzerInfoFile + ".clang-tidy-cmd"); + fcmd << istr.str(); + } + + while (std::getline(istr, line)) { + if (line.find("error") == std::string::npos && line.find("warning") == std::string::npos) + continue; + + std::size_t endColumnPos = line.find(": error:"); + if (endColumnPos == std::string::npos) { + endColumnPos = line.find(": warning:"); + } + + const std::size_t endLinePos = line.rfind(":", endColumnPos-1); + const std::size_t endNamePos = line.rfind(":", endLinePos - 1); + const std::size_t endMsgTypePos = line.find(':', endColumnPos + 2); + const std::size_t endErrorPos = line.rfind('[', std::string::npos); + + const std::string lineNumString = line.substr(endNamePos + 1, endLinePos - endNamePos - 1); + const std::string columnNumString = line.substr(endLinePos + 1, endColumnPos - endLinePos - 1); + const std::string errorTypeString = line.substr(endColumnPos + 1, endMsgTypePos - endColumnPos - 1); + const std::string messageString = line.substr(endMsgTypePos + 1, endErrorPos - endMsgTypePos - 1); + const std::string errorString = line.substr(endErrorPos, line.length()); + + std::string fixedpath = Path::simplifyPath(line.substr(0, endNamePos)); + const int64_t lineNumber = std::atol(lineNumString.c_str()); + const int64_t column = std::atol(columnNumString.c_str()); + fixedpath = Path::toNativeSeparators(fixedpath); + + ErrorLogger::ErrorMessage errmsg; + errmsg.callStack.emplace_back(ErrorLogger::ErrorMessage::FileLocation(fixedpath, lineNumber, column)); + + errmsg.id = "clang-tidy-" + errorString.substr(1, errorString.length() - 2); + if (errmsg.id.find("performance") != std::string::npos) + errmsg.severity = Severity::SeverityType::performance; + else if (errmsg.id.find("portability") != std::string::npos) + errmsg.severity = Severity::SeverityType::portability; + else if (errmsg.id.find("cert") != std::string::npos || errmsg.id.find("misc") != std::string::npos || errmsg.id.find("unused") != std::string::npos) + errmsg.severity = Severity::SeverityType::warning; + else + errmsg.severity = Severity::SeverityType::style; + + errmsg.file0 = fixedpath; + errmsg.setmsg(messageString); + reportErr(errmsg); + } +} + bool CppCheck::analyseWholeProgram() { bool errors = false; diff --git a/lib/cppcheck.h b/lib/cppcheck.h index 7f185fa04..44f8061dc 100644 --- a/lib/cppcheck.h +++ b/lib/cppcheck.h @@ -136,6 +136,9 @@ public: */ bool analyseWholeProgram(); + /** Analyze all files using clang-tidy */ + void analyseClangTidy(const ImportProject::FileSettings &fileSettings); + /** analyse whole program use .analyzeinfo files */ void analyseWholeProgram(const std::string &buildDir, const std::map &files); diff --git a/lib/importproject.cpp b/lib/importproject.cpp index 0cb8af05f..9596138ca 100644 --- a/lib/importproject.cpp +++ b/lib/importproject.cpp @@ -1026,9 +1026,13 @@ bool ImportProject::importCppcheckGuiProject(std::istream &istr, Settings *setti temp.addons = readXmlStringList(node, "", CppcheckXml::AddonElementName, nullptr); else if (strcmp(node->Name(), CppcheckXml::TagsElementName) == 0) node->Attribute(CppcheckXml::TagElementName); // FIXME: Write some warning - else if (strcmp(node->Name(), CppcheckXml::ToolsElementName) == 0) - node->Attribute(CppcheckXml::ToolElementName); // FIXME: Write some warning - else if (strcmp(node->Name(), CppcheckXml::CheckHeadersElementName) == 0) + else if (strcmp(node->Name(), CppcheckXml::ToolsElementName) == 0) { + const std::list toolList = readXmlStringList(node, "", CppcheckXml::ToolElementName, nullptr); + for (const std::string &toolName : toolList) { + if (toolName == std::string(CppcheckXml::ClangTidy)) + temp.clangTidy = true; + } + } else if (strcmp(node->Name(), CppcheckXml::CheckHeadersElementName) == 0) temp.checkHeaders = (strcmp(node->GetText(), "true") == 0); else if (strcmp(node->Name(), CppcheckXml::CheckUnusedTemplatesElementName) == 0) temp.checkUnusedTemplates = (strcmp(node->GetText(), "true") == 0); @@ -1059,6 +1063,7 @@ bool ImportProject::importCppcheckGuiProject(std::istream &istr, Settings *setti settings->userDefines = temp.userDefines; settings->userUndefs = temp.userUndefs; settings->addons = temp.addons; + settings->clangTidy = temp.clangTidy; for (const std::string &p : paths) guiProject.pathNames.push_back(p); for (const std::string &supp : suppressions) diff --git a/lib/importproject.h b/lib/importproject.h index 9e60be4e5..0a561979b 100644 --- a/lib/importproject.h +++ b/lib/importproject.h @@ -152,6 +152,7 @@ namespace CppcheckXml { const char CheckUnusedTemplatesElementName[] = "check-unused-templates"; const char MaxCtuDepthElementName[] = "max-ctu-depth"; const char CheckUnknownFunctionReturn[] = "check-unknown-function-return-values"; + const char ClangTidy[] = "clang-tidy"; const char Name[] = "name"; } diff --git a/lib/settings.cpp b/lib/settings.cpp index be45d473e..589e18437 100644 --- a/lib/settings.cpp +++ b/lib/settings.cpp @@ -35,6 +35,7 @@ Settings::Settings() checkHeaders(true), checkUnusedTemplates(false), clang(false), + clangTidy(false), debugSimplified(false), debugnormal(false), debugwarnings(false), diff --git a/lib/settings.h b/lib/settings.h index 59b183064..6321c2ab2 100644 --- a/lib/settings.h +++ b/lib/settings.h @@ -99,6 +99,9 @@ public: /** Use Clang */ bool clang; + /** Use clang-tidy */ + bool clangTidy; + /** @brief include paths excluded from checking the configuration */ std::set configExcludePaths;