diff --git a/gui/checkthread.cpp b/gui/checkthread.cpp index 45c4fe96a..88377f720 100644 --- a/gui/checkthread.cpp +++ b/gui/checkthread.cpp @@ -18,6 +18,9 @@ #include #include +#include +#include +#include #include "checkthread.h" #include "threadresult.h" #include "cppcheck.h" @@ -69,10 +72,47 @@ void CheckThread::run() return; } + QString addonPath; + if (QFileInfo(mDataDir + "/threadsafety.py").exists()) + addonPath = mDataDir; + else if (QDir(mDataDir + "/addons").exists()) + addonPath = mDataDir + "/addons"; + else if (mDataDir.endsWith("/cfg")) { + if (QDir(mDataDir.mid(0,mDataDir.size()-3) + "addons").exists()) + addonPath = mDataDir.mid(0,mDataDir.size()-3) + "addons"; + } + + bool needDump = mAddons.contains("y2038") || mAddons.contains("threadsafety") || mAddons.contains("cert") || mAddons.contains("misra"); QString file = mResult.getNextFile(); while (!file.isEmpty() && mState == Running) { qDebug() << "Checking file" << file; mCppcheck.check(file.toStdString()); + if (!mAddons.isEmpty()) { + if (needDump) { + mCppcheck.settings().dump = true; + mCppcheck.check(file.toStdString()); + mCppcheck.settings().dump = false; + } + foreach (const QString addon, mAddons) { + if (addon == "clang") + continue; + QProcess process; + QString a; + if (QFileInfo(addonPath + '/' + addon + ".py").exists()) + a = addonPath + '/' + addon + ".py"; + else if (QFileInfo(addonPath + '/' + addon + '/' + addon + ".py").exists()) + a = addonPath + '/' + addon + '/' + addon + ".py"; + else + continue; + QString dumpFile = QString::fromStdString(file + ".dump"); + QString cmd = "python " + a + ' ' + dumpFile; + qDebug() << cmd; + process.start(cmd); + process.waitForFinished(); + QString err(process.readAllStandardError()); + parseErrors(err, addon); + } + } emit fileChecked(file); if (mState == Running) @@ -84,6 +124,45 @@ void CheckThread::run() file = QString::fromStdString(fileSettings.filename); qDebug() << "Checking file" << file; mCppcheck.check(fileSettings); + if (!mAddons.isEmpty()) { + if (needDump) { + mCppcheck.settings().dump = true; + mCppcheck.check(fileSettings); + mCppcheck.settings().dump = false; + } + foreach (const QString addon, mAddons) { + QProcess process; + if (addon == "clang") { + QString cmd("clang --analyze"); + for (std::list::const_iterator I = fileSettings.includePaths.begin(); I != fileSettings.includePaths.end(); ++I) + cmd += " -I" + QString::fromStdString(*I); + foreach (QString D, QString::fromStdString(fileSettings.defines).split(";")) + cmd += " -D" + D; + QString fileName = QString::fromStdString(fileSettings.filename); + if (fileName.endsWith(".cpp")) + cmd += " -std=c++11"; + cmd += ' ' + fileName; + qDebug() << cmd; + process.start(cmd); + process.waitForFinished(600*1000); + } else { + QString a; + if (QFileInfo(addonPath + '/' + addon + ".py").exists()) + a = addonPath + '/' + addon + ".py"; + else if (QFileInfo(addonPath + '/' + addon + '/' + addon + ".py").exists()) + a = addonPath + '/' + addon + '/' + addon + ".py"; + else + continue; + QString dumpFile = QString::fromStdString(fileSettings.filename + ".dump"); + QString cmd = "python " + a + ' ' + dumpFile; + qDebug() << cmd; + process.start(cmd); + process.waitForFinished(); + } + QString err(process.readAllStandardError()); + parseErrors(err, addon); + } + } emit fileChecked(file); if (mState == Running) @@ -103,3 +182,46 @@ void CheckThread::stop() mState = Stopping; mCppcheck.terminate(); } + +void CheckThread::parseErrors(QString err, QString tool) +{ + QTextStream in(&err, QIODevice::ReadOnly); + while (!in.atEnd()) { + QString line = in.readLine(); + + if (tool == "clang") { + QRegExp r("([^:]+):([0-9]+):[0-9]+: (warning|error): (.*)"); + if (!r.exactMatch(line)) + continue; + const std::string filename = r.cap(1).toStdString(); + const int lineNumber = r.cap(2).toInt(); + Severity::SeverityType severity = (r.cap(3) == "error") ? Severity::error : Severity::warning; + const std::string message = r.cap(4).toStdString(); + const std::string id = tool.toStdString(); + std::list callstack; + callstack.push_back(ErrorLogger::ErrorMessage::FileLocation(filename, lineNumber)); + ErrorLogger::ErrorMessage errmsg(callstack, filename, severity, message, id, false); + mResult.reportErr(errmsg); + } else { + QRegExp r1("\\[([^:]+):([0-9]+)\\](.*)"); + if (!r1.exactMatch(line)) + continue; + const std::string &filename = r1.cap(1).toStdString(); + const int lineNumber = r1.cap(2).toInt(); + + std::string message, id; + QRegExp r2("(.*)\\[([a-zA-Z0-9\\-\\._]+)\\]"); + if (r2.exactMatch(r1.cap(3))) { + message = r2.cap(1).toStdString(); + id = tool.toStdString() + '-' + r2.cap(2).toStdString(); + } else { + message = r1.cap(3).toStdString(); + id = tool.toStdString(); + } + std::list callstack; + callstack.push_back(ErrorLogger::ErrorMessage::FileLocation(filename, lineNumber)); + ErrorLogger::ErrorMessage errmsg(callstack, filename, Severity::style, message, id, false); + mResult.reportErr(errmsg); + } + } +} diff --git a/gui/checkthread.h b/gui/checkthread.h index 557bb8fa0..6f627e6ae 100644 --- a/gui/checkthread.h +++ b/gui/checkthread.h @@ -52,6 +52,14 @@ public: */ void analyseWholeProgram(const QStringList &files); + void setAddons(const QStringList &addons) { + mAddons = addons; + } + + void setDataDir(const QString &dataDir) { + mDataDir = dataDir; + } + /** * @brief method that is run in a thread * @@ -94,13 +102,16 @@ protected: ThreadResult &mResult; /** * @brief Cppcheck itself - * */ CppCheck mCppcheck; private: + void parseErrors(QString err, QString tool); + QStringList mFiles; bool mAnalyseWholeProgram; + QStringList mAddons; + QString mDataDir; }; /// @} #endif // CHECKTHREAD_H diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index f2e1a842c..22eb042f8 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -66,6 +66,7 @@ MainWindow::MainWindow(TranslationHandler* th, QSettings* settings) : { mUI.setupUi(this); mThread = new ThreadHandler(this); + mThread->setDataDir(mSettings->value("DATADIR", QString()).toString()); mUI.mResults->initialize(mSettings, mApplications, mThread); // Filter timer to delay filtering results slightly while typing @@ -411,6 +412,8 @@ void MainWindow::doAnalyzeProject(ImportProject p) } //mThread->SetanalyzeProject(true); + if (mProjectFile) + mThread->setAddons(mProjectFile->getAddons()); mThread->setProject(p); mThread->check(checkSettings, true); } @@ -1364,6 +1367,8 @@ void MainWindow::analyzeProject(const ProjectFile *projectFile) QFileInfo inf(projectFile->getFilename()); const QString rootpath = projectFile->getRootPath(); + mThread->setAddons(projectFile->getAddons()); + // If the root path is not given or is not "current dir", use project // file's location directory as root path if (rootpath.isEmpty() || rootpath == ".") diff --git a/gui/projectfile.cpp b/gui/projectfile.cpp index 336595662..cc2477458 100644 --- a/gui/projectfile.cpp +++ b/gui/projectfile.cpp @@ -50,6 +50,8 @@ static const char LibrariesElementName[] = "libraries"; static const char LibraryElementName[] = "library"; static const char SuppressionsElementName[] = "suppressions"; static const char SuppressionElementName[] = "suppression"; +static const char AddonElementName[] = "addon"; +static const char AddonsElementName[] = "addons"; ProjectFile::ProjectFile(QObject *parent) : QObject(parent) @@ -122,6 +124,10 @@ bool ProjectFile::read(const QString &filename) if (insideProject && xmlReader.name() == SuppressionsElementName) readStringList(mSuppressions, xmlReader,SuppressionElementName); + // Addons + if (insideProject && xmlReader.name() == AddonsElementName) + readStringList(mAddons, xmlReader, AddonElementName); + break; case QXmlStreamReader::EndElement: @@ -433,6 +439,11 @@ void ProjectFile::setSuppressions(const QStringList &suppressions) mSuppressions = suppressions; } +void ProjectFile::setAddons(const QStringList &addons) +{ + mAddons = addons; +} + bool ProjectFile::write(const QString &filename) { if (!filename.isEmpty()) @@ -516,6 +527,11 @@ bool ProjectFile::write(const QString &filename) SuppressionsElementName, SuppressionElementName); + writeStringList(xmlWriter, + mAddons, + AddonsElementName, + AddonElementName); + xmlWriter.writeEndDocument(); file.close(); return true; diff --git a/gui/projectfile.h b/gui/projectfile.h index ac87ac95d..f8cf5d557 100644 --- a/gui/projectfile.h +++ b/gui/projectfile.h @@ -110,6 +110,14 @@ public: return mSuppressions; } + /** + * @brief Get list addons. + * @return list of addons. + */ + QStringList getAddons() const { + return mAddons; + } + /** * @brief Get filename for the project file. * @return file name. @@ -171,6 +179,12 @@ public: */ void setSuppressions(const QStringList &suppressions); + /** + * @brief Set list of addons. + * @param addons List of addons. + */ + void setAddons(const QStringList &addons); + /** * @brief Write project file (to disk). * @param filename Filename to use. @@ -297,6 +311,11 @@ private: * @brief List of suppressions. */ QStringList mSuppressions; + + /** + * @brief List of addons. + */ + QStringList mAddons; }; /// @} #endif // PROJECT_FILE_H diff --git a/gui/projectfiledialog.cpp b/gui/projectfiledialog.cpp index 70d1968b0..7f669a1e7 100644 --- a/gui/projectfiledialog.cpp +++ b/gui/projectfiledialog.cpp @@ -141,6 +141,11 @@ void ProjectFileDialog::loadFromProjectFile(const ProjectFile *projectFile) setExcludedPaths(projectFile->getExcludedPaths()); setLibraries(projectFile->getLibraries()); setSuppressions(projectFile->getSuppressions()); + mUI.mAddonThreadSafety->setChecked(projectFile->getAddons().contains("threadsafety")); + mUI.mAddonY2038->setChecked(projectFile->getAddons().contains("y2038")); + mUI.mAddonCert->setChecked(projectFile->getAddons().contains("cert")); + mUI.mAddonMisra->setChecked(projectFile->getAddons().contains("misra")); + mUI.mClang->setChecked(projectFile->getAddons().contains("clang")); updatePathsAndDefines(); } @@ -155,6 +160,18 @@ void ProjectFileDialog::saveToProjectFile(ProjectFile *projectFile) const projectFile->setExcludedPaths(getExcludedPaths()); projectFile->setLibraries(getLibraries()); projectFile->setSuppressions(getSuppressions()); + QStringList list; + if (mUI.mAddonThreadSafety->isChecked()) + list << "threadsafety"; + if (mUI.mAddonY2038->isChecked()) + list << "y2038"; + if (mUI.mAddonCert->isChecked()) + list << "cert"; + if (mUI.mAddonMisra->isChecked()) + list << "misra"; + if (mUI.mClang->isChecked()) + list << "clang"; + projectFile->setAddons(list); } void ProjectFileDialog::ok() @@ -224,7 +241,7 @@ void ProjectFileDialog::browseImportProject() const QDir &dir = inf.absoluteDir(); QString fileName = QFileDialog::getOpenFileName(this, tr("Import Project"), dir.canonicalPath(), - tr("Visual Studio (*.sln *.vcxproj);;Compile database (compile_database.json)")); + tr("Visual Studio (*.sln *.vcxproj);;Compile database (compile_commands.json)")); if (!fileName.isEmpty()) { mUI.mEditImportProject->setText(dir.relativeFilePath(fileName)); updatePathsAndDefines(); diff --git a/gui/projectfiledialog.ui b/gui/projectfiledialog.ui index f302fe59e..60988b271 100644 --- a/gui/projectfiledialog.ui +++ b/gui/projectfiledialog.ui @@ -7,7 +7,7 @@ 0 0 642 - 498 + 507 @@ -463,6 +463,75 @@ + + + Addons + + + + + + Y2038 + + + + + + + Thread safety + + + + + + + Coding standards + + + + + + + Cert + + + + + + + Misra + + + + + + + Other + + + + + + + clang + + + + + + + Qt::Vertical + + + + 20 + 368 + + + + + + diff --git a/gui/threadhandler.cpp b/gui/threadhandler.cpp index 103a473e6..9ea0775e8 100644 --- a/gui/threadhandler.cpp +++ b/gui/threadhandler.cpp @@ -46,6 +46,7 @@ void ThreadHandler::clearFiles() mLastFiles.clear(); mResults.clearFiles(); mAnalyseWholeProgram = false; + mAddons.clear(); } void ThreadHandler::setFiles(const QStringList &files) @@ -91,6 +92,8 @@ void ThreadHandler::check(const Settings &settings, bool all) } for (int i = 0; i < mRunningThreadCount; i++) { + mThreads[i]->setAddons(mAddons); + mThreads[i]->setDataDir(mDataDir); mThreads[i]->check(settings); } @@ -125,7 +128,6 @@ void ThreadHandler::setThreadCount(const int count) connect(mThreads.last(), &CheckThread::fileChecked, &mResults, &ThreadResult::fileChecked); } - } diff --git a/gui/threadhandler.h b/gui/threadhandler.h index cba40f944..8829f5cbe 100644 --- a/gui/threadhandler.h +++ b/gui/threadhandler.h @@ -71,6 +71,14 @@ public: */ void saveSettings(QSettings &settings) const; + void setAddons(const QStringList &addons) { + mAddons = addons; + } + + void setDataDir(const QString &dataDir) { + mDataDir = dataDir; + } + /** * @brief Clear all files from cppcheck * @@ -236,6 +244,10 @@ protected: int mRunningThreadCount; bool mAnalyseWholeProgram; + + QStringList mAddons; + + QString mDataDir; private: /**