Skip to content

Commit d82da98

Browse files
authored
Support clang tidy (cppcheck-opensource#2486)
1 parent dcee189 commit d82da98

7 files changed

Lines changed: 95 additions & 3 deletions

File tree

cli/cppcheckexecutor.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -925,6 +925,8 @@ int CppCheckExecutor::check_internal(CppCheck& cppcheck, int /*argc*/, const cha
925925
++c;
926926
if (!settings.quiet)
927927
reportStatus(c, settings.project.fileSettings.size(), c, settings.project.fileSettings.size());
928+
if(settings.clangTidy)
929+
cppcheck.analyseClangTidy(fs);
928930
}
929931
}
930932

lib/cppcheck.cpp

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1352,6 +1352,83 @@ void CppCheck::getErrorMessages()
13521352
Preprocessor::getErrorMessages(this, &s);
13531353
}
13541354

1355+
void CppCheck::analyseClangTidy(const ImportProject::FileSettings &fileSettings )
1356+
{
1357+
std::string allIncludes = "";
1358+
std::string allDefines = "-D"+fileSettings.defines;
1359+
for (const std::string &inc : fileSettings.includePaths) {
1360+
allIncludes = allIncludes + "-I\"" + inc + "\" ";
1361+
}
1362+
1363+
std::string::size_type pos = 0u;
1364+
while ((pos = allDefines.find(";", pos)) != std::string::npos)
1365+
{
1366+
allDefines.replace(pos, 1, " -D");
1367+
pos += 3;
1368+
}
1369+
1370+
const std::string cmd = "clang-tidy -quiet -checks=*,-clang-analyzer-*,-llvm* \"" + fileSettings.filename + "\" -- " + allIncludes + allDefines;
1371+
std::pair<bool, std::string> result = executeCommand(cmd);
1372+
if (!result.first) {
1373+
std::cerr << "Failed to execute '" + cmd + "'" << std::endl;
1374+
return;
1375+
}
1376+
1377+
// parse output and create error messages
1378+
std::istringstream istr(result.second);
1379+
std::string line;
1380+
1381+
if (!mSettings.buildDir.empty())
1382+
{
1383+
const std::string analyzerInfoFile = AnalyzerInformation::getAnalyzerInfoFile(mSettings.buildDir, fileSettings.filename, "");
1384+
std::ofstream fcmd(analyzerInfoFile + ".clang-tidy-cmd");
1385+
fcmd << istr.str();
1386+
}
1387+
1388+
while (std::getline(istr, line)) {
1389+
if (line.find("error") == std::string::npos && line.find("warning") == std::string::npos)
1390+
continue;
1391+
1392+
std::size_t endColumnPos = line.find(": error:");
1393+
if (endColumnPos == std::string::npos) {
1394+
endColumnPos = line.find(": warning:");
1395+
}
1396+
1397+
const std::size_t endLinePos = line.rfind(":", endColumnPos-1);
1398+
const std::size_t endNamePos = line.rfind(":", endLinePos - 1);
1399+
const std::size_t endMsgTypePos = line.find(':', endColumnPos + 2);
1400+
const std::size_t endErrorPos = line.rfind('[', std::string::npos);
1401+
1402+
const std::string lineNumString = line.substr(endNamePos + 1, endLinePos - endNamePos - 1);
1403+
const std::string columnNumString = line.substr(endLinePos + 1, endColumnPos - endLinePos - 1);
1404+
const std::string errorTypeString = line.substr(endColumnPos + 1, endMsgTypePos - endColumnPos - 1);
1405+
const std::string messageString = line.substr(endMsgTypePos + 1, endErrorPos - endMsgTypePos - 1);
1406+
const std::string errorString = line.substr(endErrorPos, line.length());
1407+
1408+
std::string fixedpath = Path::simplifyPath(line.substr(0, endNamePos));
1409+
const int64_t lineNumber = std::atol(lineNumString.c_str());
1410+
const int64_t column = std::atol(columnNumString.c_str());
1411+
fixedpath = Path::toNativeSeparators(fixedpath);
1412+
1413+
ErrorLogger::ErrorMessage errmsg;
1414+
errmsg.callStack.emplace_back(ErrorLogger::ErrorMessage::FileLocation(fixedpath, lineNumber, column));
1415+
1416+
errmsg.id = "clang-tidy-" + errorString.substr(1, errorString.length() - 2);
1417+
if (errmsg.id.find("performance") != std::string::npos)
1418+
errmsg.severity = Severity::SeverityType::performance;
1419+
else if (errmsg.id.find("portability") != std::string::npos)
1420+
errmsg.severity = Severity::SeverityType::portability;
1421+
else if (errmsg.id.find("cert") != std::string::npos || errmsg.id.find("misc") != std::string::npos || errmsg.id.find("unused") != std::string::npos)
1422+
errmsg.severity = Severity::SeverityType::warning;
1423+
else
1424+
errmsg.severity = Severity::SeverityType::style;
1425+
1426+
errmsg.file0 = fixedpath;
1427+
errmsg.setmsg(messageString);
1428+
reportErr(errmsg);
1429+
}
1430+
}
1431+
13551432
bool CppCheck::analyseWholeProgram()
13561433
{
13571434
bool errors = false;

lib/cppcheck.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,9 @@ class CPPCHECKLIB CppCheck : ErrorLogger {
136136
*/
137137
bool analyseWholeProgram();
138138

139+
/** Analyze all files using clang-tidy */
140+
void analyseClangTidy(const ImportProject::FileSettings &fileSettings);
141+
139142
/** analyse whole program use .analyzeinfo files */
140143
void analyseWholeProgram(const std::string &buildDir, const std::map<std::string, std::size_t> &files);
141144

lib/importproject.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1026,9 +1026,13 @@ bool ImportProject::importCppcheckGuiProject(std::istream &istr, Settings *setti
10261026
temp.addons = readXmlStringList(node, "", CppcheckXml::AddonElementName, nullptr);
10271027
else if (strcmp(node->Name(), CppcheckXml::TagsElementName) == 0)
10281028
node->Attribute(CppcheckXml::TagElementName); // FIXME: Write some warning
1029-
else if (strcmp(node->Name(), CppcheckXml::ToolsElementName) == 0)
1030-
node->Attribute(CppcheckXml::ToolElementName); // FIXME: Write some warning
1031-
else if (strcmp(node->Name(), CppcheckXml::CheckHeadersElementName) == 0)
1029+
else if (strcmp(node->Name(), CppcheckXml::ToolsElementName) == 0) {
1030+
const std::list<std::string> toolList = readXmlStringList(node, "", CppcheckXml::ToolElementName, nullptr);
1031+
for (const std::string &toolName : toolList) {
1032+
if (toolName == std::string(CppcheckXml::ClangTidy))
1033+
temp.clangTidy = true;
1034+
}
1035+
} else if (strcmp(node->Name(), CppcheckXml::CheckHeadersElementName) == 0)
10321036
temp.checkHeaders = (strcmp(node->GetText(), "true") == 0);
10331037
else if (strcmp(node->Name(), CppcheckXml::CheckUnusedTemplatesElementName) == 0)
10341038
temp.checkUnusedTemplates = (strcmp(node->GetText(), "true") == 0);
@@ -1059,6 +1063,7 @@ bool ImportProject::importCppcheckGuiProject(std::istream &istr, Settings *setti
10591063
settings->userDefines = temp.userDefines;
10601064
settings->userUndefs = temp.userUndefs;
10611065
settings->addons = temp.addons;
1066+
settings->clangTidy = temp.clangTidy;
10621067
for (const std::string &p : paths)
10631068
guiProject.pathNames.push_back(p);
10641069
for (const std::string &supp : suppressions)

lib/importproject.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ namespace CppcheckXml {
152152
const char CheckUnusedTemplatesElementName[] = "check-unused-templates";
153153
const char MaxCtuDepthElementName[] = "max-ctu-depth";
154154
const char CheckUnknownFunctionReturn[] = "check-unknown-function-return-values";
155+
const char ClangTidy[] = "clang-tidy";
155156
const char Name[] = "name";
156157
}
157158

lib/settings.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ Settings::Settings()
3535
checkHeaders(true),
3636
checkUnusedTemplates(false),
3737
clang(false),
38+
clangTidy(false),
3839
debugSimplified(false),
3940
debugnormal(false),
4041
debugwarnings(false),

lib/settings.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ class CPPCHECKLIB Settings : public cppcheck::Platform {
9999
/** Use Clang */
100100
bool clang;
101101

102+
/** Use clang-tidy */
103+
bool clangTidy;
104+
102105
/** @brief include paths excluded from checking the configuration */
103106
std::set<std::string> configExcludePaths;
104107

0 commit comments

Comments
 (0)