File indexing completed on 2025-01-05 05:20:14
0001 /* This file is part of the Kate project. 0002 * 0003 * SPDX-FileCopyrightText: 2021 Waqar Ahmed <waqar.17a@gmail.com> 0004 * 0005 * SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 #include "clazy.h" 0008 #include "kateproject.h" 0009 0010 #include <KLocalizedString> 0011 0012 #include <QDir> 0013 #include <QRegularExpression> 0014 0015 KateProjectCodeAnalysisToolClazy::KateProjectCodeAnalysisToolClazy(QObject *parent) 0016 : KateProjectCodeAnalysisTool(parent) 0017 { 0018 } 0019 0020 QString KateProjectCodeAnalysisToolClazy::name() const 0021 { 0022 return i18n("Clazy (Qt/C++)"); 0023 } 0024 0025 QString KateProjectCodeAnalysisToolClazy::description() const 0026 { 0027 return i18n("Clazy is a static analysis tool for Qt/C++ code"); 0028 } 0029 0030 QString KateProjectCodeAnalysisToolClazy::fileExtensions() const 0031 { 0032 return QStringLiteral("cpp|cxx|cc|c++|tpp|txx"); 0033 } 0034 0035 QStringList KateProjectCodeAnalysisToolClazy::filter(const QStringList &files) const 0036 { 0037 // c++ files 0038 return files.filter( 0039 QRegularExpression(QStringLiteral("\\.(") + fileExtensions().replace(QStringLiteral("+"), QStringLiteral("\\+")) + QStringLiteral(")$"))); 0040 } 0041 0042 QString KateProjectCodeAnalysisToolClazy::path() const 0043 { 0044 return QStringLiteral("clazy-standalone"); 0045 } 0046 0047 static QString buildDirectory(const QVariantMap &projectMap) 0048 { 0049 const QVariantMap buildMap = projectMap[QStringLiteral("build")].toMap(); 0050 const QString buildDir = buildMap[QStringLiteral("directory")].toString(); 0051 return buildDir; 0052 } 0053 0054 QStringList KateProjectCodeAnalysisToolClazy::arguments() 0055 { 0056 if (!m_project) { 0057 return {}; 0058 } 0059 0060 QString compileCommandsDir = compileCommandsDirectory(); 0061 0062 QStringList args; 0063 if (!compileCommandsDir.isEmpty()) { 0064 args = QStringList{QStringLiteral("-p"), compileCommandsDir}; 0065 } 0066 0067 const QStringList fileList = filter(m_project->files()); 0068 setActualFilesCount(fileList.size()); 0069 0070 return args << fileList; 0071 } 0072 0073 QString KateProjectCodeAnalysisToolClazy::notInstalledMessage() const 0074 { 0075 return i18n("Please install 'clazy'."); 0076 } 0077 0078 FileDiagnostics KateProjectCodeAnalysisToolClazy::parseLine(const QString &line) const 0079 { 0080 //"/path/kate/kate/kateapp.cpp:529:10: warning: Missing reference in range-for with non trivial type (QJsonValue) [-Wclazy-range-loop]" 0081 int idxColon = line.indexOf(QLatin1Char(':')); 0082 if (idxColon < 0) { 0083 return {}; 0084 } 0085 // File 0086 const QString file = line.mid(0, idxColon); 0087 idxColon++; 0088 0089 // Line 0090 int nextColon = line.indexOf(QLatin1Char(':'), idxColon); 0091 if (nextColon < 0) { 0092 return {}; 0093 } 0094 const QString lineNo = line.mid(idxColon, nextColon - idxColon); 0095 bool ok = true; 0096 lineNo.toInt(&ok); 0097 if (!ok) { 0098 return {}; 0099 } 0100 idxColon = nextColon + 1; 0101 0102 // Column 0103 nextColon = line.indexOf(QLatin1Char(':'), idxColon); 0104 const QString columnNo = line.mid(idxColon, nextColon - idxColon); 0105 idxColon = nextColon + 1; 0106 0107 int spaceIdx = line.indexOf(QLatin1Char(' '), nextColon); 0108 if (spaceIdx < 0) { 0109 return {}; 0110 } 0111 0112 idxColon = line.indexOf(QLatin1Char(':'), spaceIdx); 0113 if (idxColon < 0) { 0114 return {}; 0115 } 0116 0117 const QString severity = line.mid(spaceIdx + 1, idxColon - (spaceIdx + 1)); 0118 0119 idxColon++; 0120 QString msg = line.mid(idxColon); 0121 0122 // Code e.g [-Wclazy-range-loop] 0123 QString code; 0124 { 0125 int bracketOpen = msg.lastIndexOf(QLatin1Char('[')); 0126 int bracketClose = msg.lastIndexOf(QLatin1Char(']')); 0127 if (bracketOpen > 0 && bracketClose > 0) { 0128 code = msg.mid(bracketOpen + 1, bracketClose - bracketOpen); 0129 // remove code from msg 0130 msg.remove(bracketOpen, (bracketClose - bracketOpen) + 1); 0131 } 0132 } 0133 0134 const auto url = QUrl::fromLocalFile(file); 0135 Diagnostic d; 0136 d.message = msg; 0137 d.severity = DiagnosticSeverity::Warning; 0138 d.code = code; 0139 int ln = lineNo.toInt() - 1; 0140 int col = columnNo.toInt() - 1; 0141 col = col < 0 ? 0 : col; 0142 d.range = KTextEditor::Range(ln, col, ln, col); 0143 return {url, {d}}; 0144 } 0145 0146 QString KateProjectCodeAnalysisToolClazy::stdinMessages() 0147 { 0148 return QString(); 0149 } 0150 0151 QString KateProjectCodeAnalysisToolClazy::compileCommandsDirectory() const 0152 { 0153 QString buildDir = buildDirectory(m_project->projectMap()); 0154 const QString compCommandsFile = QStringLiteral("compile_commands.json"); 0155 0156 if (buildDir.startsWith(QLatin1String("./"))) { 0157 buildDir = buildDir.mid(2); 0158 } 0159 0160 /** 0161 * list of absoloute paths to check compile commands 0162 */ 0163 const QString possiblePaths[4] = { 0164 /** Absoloute build path in .kateproject e.g from cmake */ 0165 buildDir, 0166 /** Relative path in .kateproject e.g */ 0167 m_project->baseDir() + (buildDir.startsWith(QLatin1Char('/')) ? buildDir : QLatin1Char('/') + buildDir), 0168 /** Check for the commonly existing "build/" directory */ 0169 m_project->baseDir() + QStringLiteral("/build"), 0170 /** Project base, maybe it has a symlink to compile_commands.json file */ 0171 m_project->baseDir(), 0172 }; 0173 0174 /** 0175 * Check all paths one by one for compile_commands.json and exit when found 0176 */ 0177 QString compileCommandsDir; 0178 for (const QString &path : possiblePaths) { 0179 if (path.isEmpty()) { 0180 continue; 0181 } 0182 const QString guessedPath = QDir(path).filePath(compCommandsFile); 0183 const bool dirHasCompileComds = QFile::exists(guessedPath); 0184 if (dirHasCompileComds) { 0185 compileCommandsDir = guessedPath; 0186 break; 0187 } 0188 } 0189 0190 return compileCommandsDir; 0191 }