50 #include <QRegularExpression>
52 #include <QTextStream>
55 #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
65 for (
const std::string &arg: args)
66 args2 << QString::fromStdString(arg);
69 process.start(QString::fromStdString(exe), args2);
70 process.waitForFinished();
72 if (redirect ==
"2>&1") {
73 QString s1 = process.readAllStandardOutput();
74 QString s2 = process.readAllStandardError();
75 output = (s1 +
"\n" + s2).toStdString();
77 output = process.readAllStandardOutput().toStdString();
80 std::ofstream fout(redirect.substr(3));
81 fout << process.readAllStandardError().toStdString();
83 return process.exitCode();
89 mCppcheck(result, true, executeCommand)
113 qDebug() <<
"Whole program analysis";
114 std::list<FileWithDetails> files2;
115 std::transform(
mFiles.cbegin(),
mFiles.cend(), std::back_inserter(files2), [&](
const QString& file) {
116 return FileWithDetails{file.toStdString(), 0};
124 QString file = mResult.getNextFile();
125 while (!file.isEmpty() && mState == Running) {
126 qDebug() <<
"Checking file" << file;
127 mCppcheck.check(file.toStdString());
128 runAddonsAndTools(
nullptr, file);
129 emit fileChecked(file);
131 if (mState == Running)
132 file = mResult.getNextFile();
135 FileSettings fileSettings = mResult.getNextFileSettings();
136 while (!fileSettings.
filename().empty() && mState == Running) {
137 file = QString::fromStdString(fileSettings.
filename());
138 qDebug() <<
"Checking file" << file;
139 mCppcheck.check(fileSettings);
140 runAddonsAndTools(&fileSettings, QString::fromStdString(fileSettings.
filename()));
141 emit fileChecked(file);
143 if (mState == Running)
144 fileSettings = mResult.getNextFileSettings();
147 if (mState == Running)
166 for (std::list<std::string>::const_iterator incIt = fileSettings->
includePaths.cbegin(); incIt != fileSettings->
includePaths.cend(); ++incIt)
167 args << (
"-I" + QString::fromStdString(*incIt));
169 args <<
"-isystem" << QString::fromStdString(*i);
170 for (
const QString& def : QString::fromStdString(fileSettings->
defines).split(
";")) {
171 args << (
"-D" + def);
173 for (
const std::string& U : fileSettings->
undefs) {
174 args << QString::fromStdString(
"-U" + U);
178 if (!clangPath.isEmpty()) {
179 QDir dir(clangPath +
"/../lib/clang");
180 for (
const QString& ver : dir.entryList()) {
181 QString includePath = dir.absolutePath() +
'/' + ver +
"/include";
182 if (ver[0] !=
'.' && QDir(includePath).exists()) {
183 args <<
"-isystem" << includePath;
194 if (!includePath.isEmpty()) {
195 includePath.replace(
"\\",
"/");
196 args <<
"-isystem" << includePath.trimmed();
200 args <<
"-U__STDC__" <<
"-fno-ms-compatibility";
203 if (!fileSettings->
standard.empty())
204 args << (
"-std=" + QString::fromStdString(fileSettings->
standard));
209 args << (
"-std=" + QString::fromStdString(std));
213 QString analyzerInfoFile;
216 if (!buildDir.empty()) {
219 QStringList args2(args);
220 args2.insert(0,
"-E");
224 process.waitForFinished();
225 const QByteArray &ba = process.readAllStandardOutput();
226 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
227 const quint16 chksum = qChecksum(QByteArrayView(ba));
229 const quint16 chksum = qChecksum(ba.data(), ba.length());
232 QFile f1(analyzerInfoFile +
'.' + addon +
"-E");
233 if (f1.open(QIODevice::ReadOnly | QIODevice::Text)) {
234 QTextStream in1(&f1);
235 const quint16 oldchksum = in1.readAll().toInt();
236 if (oldchksum == chksum) {
237 QFile f2(analyzerInfoFile +
'.' + addon +
"-results");
238 if (f2.open(QIODevice::ReadOnly | QIODevice::Text)) {
239 QTextStream in2(&f2);
246 f1.open(QIODevice::WriteOnly | QIODevice::Text);
247 QTextStream out1(&f1);
250 QFile::remove(analyzerInfoFile +
'.' + addon +
"-results");
262 args.insert(0,
"-checks=-*,clang-analyzer-*");
263 args.insert(1, fileName);
264 args.insert(2,
"--");
266 args.insert(0,
"-checks=*,-clang-analyzer-*,-llvm*");
267 args.insert(1, fileName);
268 args.insert(2,
"--");
273 QString
debug(cmd.contains(
" ") ? (
'\"' + cmd +
'\"') : cmd);
274 for (
const QString& arg : args) {
275 if (arg.contains(
" "))
276 debug +=
" \"" + arg +
'\"';
282 if (!analyzerInfoFile.isEmpty()) {
283 QFile f(analyzerInfoFile +
'.' + addon +
"-cmd");
284 if (f.open(QIODevice::WriteOnly | QIODevice::Text)) {
293 process.waitForFinished(600*1000);
294 const QString errout(process.readAllStandardOutput() +
"\n\n\n" + process.readAllStandardError());
295 if (!analyzerInfoFile.isEmpty()) {
296 QFile f(analyzerInfoFile +
'.' + addon +
"-results");
297 if (f.open(QIODevice::WriteOnly | QIODevice::Text)) {
316 QList<ErrorItem> errorItems;
318 static const QRegularExpression r1(
"^(.+):([0-9]+):([0-9]+): (note|warning|error|fatal error): (.*)$");
319 static const QRegularExpression r2(
"^(.*)\\[([a-zA-Z0-9\\-_\\.]+)\\]$");
320 QTextStream in(&err, QIODevice::ReadOnly);
321 while (!in.atEnd()) {
322 QString line = in.readLine();
324 if (line.startsWith(
"Assertion failed:")) {
330 e.
errorId = tool +
"-internal-error";
334 errorItems.append(e);
338 const QRegularExpressionMatch r1MatchRes = r1.match(line);
339 if (!r1MatchRes.hasMatch())
341 if (r1MatchRes.captured(4) !=
"note") {
342 errorItems.append(errorItem);
344 errorItem.
file0 = r1MatchRes.captured(1);
348 errorItem.
errorPath.last().file = r1MatchRes.captured(1);
349 errorItem.
errorPath.last().line = r1MatchRes.captured(2).toInt();
350 errorItem.
errorPath.last().column = r1MatchRes.captured(3).toInt();
351 if (r1MatchRes.captured(4) ==
"warning")
353 else if (r1MatchRes.captured(4) ==
"error" || r1MatchRes.captured(4) ==
"fatal error")
357 const QRegularExpressionMatch r2MatchRes = r2.match(r1MatchRes.captured(5));
358 if (r2MatchRes.hasMatch()) {
359 message = r2MatchRes.captured(1);
360 const QString id1(r2MatchRes.captured(2));
361 if (id1.startsWith(
"clang"))
364 id = tool +
'-' + r2MatchRes.captured(2);
366 if (id1.startsWith(
"performance"))
368 else if (id1.startsWith(
"portability"))
370 else if (id1.startsWith(
"misc") && !id1.contains(
"unused"))
376 message = r1MatchRes.captured(5);
385 errorItem.
errorPath.last().info = message;
387 errorItems.append(errorItem);
390 if (e.errorPath.isEmpty())
393 errorMessage.
setFileName(e.errorPath.back().file.toStdString());
394 errorMessage.
lineNumber = e.errorPath.back().line;
395 errorMessage.
errorId = e.errorId.toStdString();
396 errorMessage.
symbolNames = e.symbolNames.toStdString();
401 std::list<ErrorMessage::FileLocation> callstack;
402 std::transform(e.errorPath.cbegin(), e.errorPath.cend(), std::back_inserter(callstack), [](
const QErrorPathItem& path) {
403 return ErrorMessage::FileLocation(path.file.toStdString(), path.info.toStdString(), path.line, path.column);
405 const std::string f0 = file0.toStdString();
406 const std::string msg = e.message.toStdString();
407 const std::string
id = e.errorId.toStdString();
416 return s.isSuppressed(errorMessage);
431 process.start(path, QStringList() <<
"--version");
432 process.waitForFinished();
433 if (process.exitCode() == 0)
438 if (QFileInfo(
"C:/Program Files/LLVM/bin/clang.exe").exists())
439 return "C:/Program Files/LLVM/bin/clang.exe";
450 path +=
"clang-tidy";
456 process.start(path, QStringList() <<
"--version");
457 process.waitForFinished();
458 if (process.exitCode() == 0)
463 if (QFileInfo(
"C:/Program Files/LLVM/bin/clang-tidy.exe").exists())
464 return "C:/Program Files/LLVM/bin/clang-tidy.exe";
bool mAnalyseWholeProgram
static QString clangTidyCmd()
Determine command to run clang-tidy.
void parseClangErrors(const QString &tool, const QString &file0, QString err)
@ Running
The thread is checking.
@ Stopping
The thread will stop after current work.
void run() override
method that is run in a thread
void analyseWholeProgram(const QStringList &files)
Run whole program analysis.
CheckThread(ThreadResult &result)
QStringList mAddonsAndTools
CppCheck mCppcheck
Cppcheck itself.
void check(const Settings &settings)
Set settings for cppcheck.
void done()
cpp checking is done
static int executeCommand(std::string exe, std::vector< std::string > args, std::string redirect, std::string &output)
void runAddonsAndTools(const FileSettings *fileSettings, const QString &fileName)
QStringList mClangIncludePaths
QList< SuppressionList::Suppression > mSuppressions
bool isSuppressed(const SuppressionList::ErrorMessage &errorMessage) const
std::atomic< State > mState
Thread's current execution state.
static QString clangCmd()
Determine command to run clang.
bool analyseWholeProgram()
Analyse whole program, run this after all TUs has been scanned.
Settings & settings()
Get reference to current settings.
A class containing error data for one error.
QList< QErrorPathItem > errorPath
Wrapper for error messages, provided by reportErr()
A class containing data for one error path item.
This is just a container for general settings so that we don't need to pass individual values to func...
static void terminate(bool t=true)
Request termination of checking.
std::string buildDir
–cppcheck-build-dir.
Standards standards
Struct contains standards settings.
Threads use this class to obtain new files to process and to publish results.
void reportErr(const ErrorMessage &msg) override
Information about found errors and warnings is directed here.
@ portability
Portability warning.
@ information
Checking information.
@ performance
Performance warning.
@ error
Programming error.
#define SETTINGS_CLANG_PATH
const std::string & filename() const
std::set< std::string > undefs
std::list< std::string > includePaths
std::list< std::string > systemIncludePaths
std::string getCPP() const
void setFileName(std::string s)
bool startsWith(const std::string &str, const char start[], std::size_t startlen)