File indexing completed on 2024-04-28 04:38:10
0001 /* 0002 SPDX-FileCopyrightText: 2016 Carlos Nihelton <carlosnsoliveira@gmail.com> 0003 SPDX-FileCopyrightText: 2018 Friedrich W. H. Kossebau <kossebau@kde.org> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "job.h" 0009 0010 // KDevPlatform 0011 #include <interfaces/icore.h> 0012 #include <interfaces/iuicontroller.h> 0013 #include <sublime/message.h> 0014 // KF 0015 #include <KLocalizedString> 0016 // Qt 0017 #include <QApplication> 0018 #include <QFile> 0019 #include <QRegularExpression> 0020 0021 namespace ClangTidy 0022 { 0023 0024 // uses ' for quoting 0025 QString inlineYaml(const Job::Parameters& parameters) 0026 { 0027 QString result; 0028 0029 result.append(QLatin1String("{Checks: '") + parameters.enabledChecks + QLatin1Char('\'')); 0030 0031 if (!parameters.headerFilter.isEmpty()) { 0032 // TODO: the regex might need escpaing for potential quotes of all kinds 0033 result.append(QLatin1String(", HeaderFilterRegex: '") + parameters.headerFilter + QLatin1Char('\'')); 0034 } 0035 result.append(QLatin1Char('}')); 0036 0037 return result; 0038 } 0039 0040 // uses " for quoting 0041 QStringList commandLineArgs(const Job::Parameters& parameters) 0042 { 0043 QStringList args{ 0044 QLatin1Char('\"') + parameters.executablePath + QLatin1Char('\"'), 0045 QLatin1String("-p=\"") + parameters.buildDir + QLatin1Char('\"'), 0046 // don't add statistics we are not interested in to parse anyway 0047 QStringLiteral("-quiet"), 0048 }; 0049 if (!parameters.additionalParameters.isEmpty()) { 0050 args << parameters.additionalParameters; 0051 } 0052 if (parameters.checkSystemHeaders) { 0053 args << QStringLiteral("--system-headers"); 0054 } 0055 0056 if (!parameters.useConfigFile) { 0057 args << QLatin1String("--config=\"") + inlineYaml(parameters) + QLatin1Char('\"'); 0058 } 0059 0060 return args; 0061 } 0062 0063 0064 Job::Job(const Parameters& params, QObject* parent) 0065 : KDevelop::CompileAnalyzeJob(parent) 0066 , m_parameters(params) 0067 { 0068 setJobName(i18n("Clang-Tidy Analysis")); 0069 0070 setParallelJobCount(m_parameters.parallelJobCount); 0071 setBuildDirectoryRoot(m_parameters.buildDir); 0072 const auto commandLine = commandLineArgs(m_parameters); 0073 setCommand(commandLine.join(QLatin1Char(' ')), false); 0074 setToolDisplayName(QStringLiteral("Clang-Tidy")); 0075 setSources(m_parameters.filePaths); 0076 0077 connect(&m_parser, &ClangTidyParser::problemsDetected, 0078 this, &Job::problemsDetected); 0079 0080 qCDebug(KDEV_CLANGTIDY) << "checking files" << params.filePaths; 0081 } 0082 0083 Job::~Job() 0084 { 0085 } 0086 0087 void Job::processStdoutLines(const QStringList& lines) 0088 { 0089 m_parser.addData(lines); 0090 m_standardOutput << lines; 0091 } 0092 0093 void Job::processStderrLines(const QStringList& lines) 0094 { 0095 static const auto xmlStartRegex = QRegularExpression(QStringLiteral("\\s*<")); 0096 0097 for (const QString& line : lines) { 0098 // unfortunately sometime clangtidy send non-XML messages to stderr. 0099 // For example, if we pass '-I /missing_include_dir' to the argument list, 0100 // then stderr output will contains such line (tested on clangtidy 1.72): 0101 // 0102 // (information) Couldn't find path given by -I '/missing_include_dir' 0103 // 0104 // Therefore we must 'move' such messages to m_standardOutput. 0105 0106 if (line.indexOf(xmlStartRegex) != -1) { // the line contains XML 0107 m_xmlOutput << line; 0108 } else { 0109 m_standardOutput << line; 0110 } 0111 } 0112 } 0113 0114 void Job::postProcessStdout(const QStringList& lines) 0115 { 0116 processStdoutLines(lines); 0117 0118 KDevelop::CompileAnalyzeJob::postProcessStdout(lines); 0119 } 0120 0121 void Job::postProcessStderr(const QStringList& lines) 0122 { 0123 processStderrLines(lines); 0124 0125 KDevelop::CompileAnalyzeJob::postProcessStderr(lines); 0126 } 0127 0128 void Job::start() 0129 { 0130 m_standardOutput.clear(); 0131 m_xmlOutput.clear(); 0132 0133 KDevelop::CompileAnalyzeJob::start(); 0134 } 0135 0136 void Job::childProcessError(QProcess::ProcessError processError) 0137 { 0138 QString messageText; 0139 0140 switch (processError) { 0141 case QProcess::FailedToStart: { 0142 messageText = i18n("Failed to start Clang-Tidy process."); 0143 break; 0144 } 0145 0146 case QProcess::Crashed: 0147 messageText = i18n("Clang-Tidy crashed."); 0148 break; 0149 0150 case QProcess::Timedout: 0151 messageText = i18n("Clang-Tidy process timed out."); 0152 break; 0153 0154 case QProcess::WriteError: 0155 messageText = i18n("Write to Clang-Tidy process failed."); 0156 break; 0157 0158 case QProcess::ReadError: 0159 messageText = i18n("Read from Clang-Tidy process failed."); 0160 break; 0161 0162 case QProcess::UnknownError: 0163 // current clangtidy errors will be displayed in the output view 0164 // don't notify the user 0165 break; 0166 } 0167 0168 if (!messageText.isEmpty()) { 0169 auto* message = new Sublime::Message(messageText, Sublime::Message::Error); 0170 KDevelop::ICore::self()->uiController()->postMessage(message); 0171 } 0172 0173 KDevelop::CompileAnalyzeJob::childProcessError(processError); 0174 } 0175 0176 void Job::childProcessExited(int exitCode, QProcess::ExitStatus exitStatus) 0177 { 0178 if (exitCode != 0) { 0179 qCDebug(KDEV_CLANGTIDY) << "clang-tidy failed, standard output: "; 0180 qCDebug(KDEV_CLANGTIDY) << m_standardOutput.join(QLatin1Char('\n')); 0181 qCDebug(KDEV_CLANGTIDY) << "clang-tidy failed, XML output: "; 0182 qCDebug(KDEV_CLANGTIDY) << m_xmlOutput.join(QLatin1Char('\n')); 0183 } 0184 0185 KDevelop::CompileAnalyzeJob::childProcessExited(exitCode, exitStatus); 0186 } 0187 0188 } // namespace ClangTidy 0189 0190 #include "moc_job.cpp"