From 547d1b158e7bae13b7d0082f4d38e8f1408bd4ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Sun, 17 May 2020 16:50:02 +0200 Subject: [PATCH] Fixed #9723 (GUI: inline suppressions does not work for addons) --- gui/checkthread.cpp | 152 -------------------------------------- gui/checkthread.h | 18 ----- gui/mainwindow.cpp | 30 +++++++- gui/projectfile.cpp | 18 +++++ gui/projectfile.h | 2 + gui/projectfiledialog.cpp | 2 +- gui/threadhandler.cpp | 1 - gui/threadhandler.h | 4 +- lib/cppcheck.cpp | 60 ++++++++------- lib/settings.h | 1 + 10 files changed, 85 insertions(+), 203 deletions(-) diff --git a/gui/checkthread.cpp b/gui/checkthread.cpp index 6d5699938..37f606bd4 100644 --- a/gui/checkthread.cpp +++ b/gui/checkthread.cpp @@ -269,75 +269,6 @@ void CheckThread::runAddonsAndTools(const ImportProject::FileSettings *fileSetti } parseClangErrors(addon, fileName, errout); - } else { - const QString python = CheckThread::pythonCmd(); - if (python.isEmpty()) - continue; - - const QString addonFilePath = CheckThread::getAddonFilePath(mDataDir, addon + ".py"); - if (addonFilePath.isEmpty()) - continue; - - if (dumpFile.isEmpty()) { - const std::string buildDir = mCppcheck.settings().buildDir; - mCppcheck.settings().buildDir.clear(); - mCppcheck.settings().dump = true; - if (!buildDir.empty()) { - mCppcheck.settings().dumpFile = AnalyzerInformation::getAnalyzerInfoFile(buildDir, fileName.toStdString(), fileSettings ? fileSettings->cfg : std::string()) + ".dump"; - dumpFile = QString::fromStdString(mCppcheck.settings().dumpFile); - } else { - dumpFile = fileName + ".dump"; - } - if (fileSettings) - mCppcheck.check(*fileSettings); - else - mCppcheck.check(fileName.toStdString()); - mCppcheck.settings().dump = false; - mCppcheck.settings().dumpFile.clear(); - mCppcheck.settings().buildDir = buildDir; - } - - QStringList args; - args << addonFilePath << "--cli" << dumpFile; - if (addon == "misra" && !mMisraFile.isEmpty() && QFileInfo(mMisraFile).exists()) { - if (mMisraFile.endsWith(".pdf", Qt::CaseInsensitive)) - args << "--misra-pdf=" + mMisraFile; - else - args << "--rule-texts=" + mMisraFile; - } - qDebug() << python << args; - - QProcess process; - QProcessEnvironment env = process.processEnvironment(); - if (!env.contains("PYTHONHOME") && !python.startsWith("python")) { - env.insert("PYTHONHOME", QFileInfo(python).canonicalPath()); - process.setProcessEnvironment(env); - } - process.start(python, args); - if (!process.waitForFinished()) { - const QString errMsg("ERROR: Process '" + python + " " + args.join(" ") + - "' did not finish successfully: " + process.errorString()); - qWarning() << errMsg; - mResult.reportOut(errMsg.toStdString()); - } - const QByteArray errout = process.readAllStandardError(); - if (process.exitCode() != 0 && !errout.isEmpty()) { - const QString errMsg("ERROR: Process '" + python + " " + args.join(" ") + - "' failed with error code " + - QString::number(process.exitCode()) + ": '" + - process.errorString() + - "'\nError output: " + errout); - qWarning() << errMsg; - mResult.reportOut(errMsg.toStdString()); - } - const QString output(process.readAllStandardOutput()); - QFile f(dumpFile + '-' + addon + "-results"); - if (f.open(QIODevice::WriteOnly | QIODevice::Text)) { - QTextStream out(&f); - out << output; - f.close(); - } - parseAddonErrors(output, addon); } } @@ -352,47 +283,6 @@ void CheckThread::stop() mCppcheck.terminate(); } -void CheckThread::parseAddonErrors(QString err, const QString &tool) -{ - Q_UNUSED(tool) - QTextStream in(&err, QIODevice::ReadOnly); - while (!in.atEnd()) { - QString line = in.readLine(); - if (!line.startsWith("{")) - continue; - - const QJsonDocument doc = QJsonDocument::fromJson(line.toLocal8Bit()); - const QJsonObject obj = doc.object(); - - /* - msg = { 'file': location.file, - 'linenr': location.linenr, - 'column': location.column, - 'severity': severity, - 'message': message, - 'addon': addon, - 'errorId': errorId, - 'extra': extra} - */ - - const std::string &filename = obj["file"].toString().toStdString(); - const int lineNumber = obj["linenr"].toInt(); - const int column = obj["column"].toInt(); - const std::string severity = obj["severity"].toString().toStdString(); - const std::string message = obj["message"].toString().toStdString(); - const std::string id = (obj["addon"].toString() + "-" + obj["errorId"].toString()).toStdString(); - - std::list callstack; - callstack.push_back(ErrorLogger::ErrorMessage::FileLocation(filename, lineNumber, column)); - ErrorLogger::ErrorMessage errmsg(callstack, filename, Severity::fromString(severity), message, id, false); - - if (isSuppressed(errmsg.toSuppressionsErrorMessage())) - continue; - - mResult.reportErr(errmsg); - } -} - void CheckThread::parseClangErrors(const QString &tool, const QString &file0, QString err) { QList errorItems; @@ -548,45 +438,3 @@ QString CheckThread::clangTidyCmd() return QString(); } - -QString CheckThread::pythonCmd() -{ - QString path = QSettings().value(SETTINGS_PYTHON_PATH).toString(); - if (!path.isEmpty()) - return path; - - path = "python"; -#ifdef Q_OS_WIN - path += ".exe"; -#endif - - QProcess process; - process.start(path, QStringList() << "--version"); - process.waitForFinished(); - if (process.exitCode() == 0) - return path; - - return QString(); -} - -QString CheckThread::getAddonFilePath(const QString &dataDir, const QString &addonFile) -{ - const QStringList paths = QStringList() << "/" << "/addons/" << "/../addons/"; - - if (!dataDir.isEmpty()) { - foreach (const QString p, paths) { - const QString filePath(dataDir + p + addonFile); - if (QFileInfo(filePath).exists()) - return filePath; - } - } - - const QString appPath = QApplication::applicationDirPath(); - foreach (const QString p, paths) { - const QString filePath(appPath + p + addonFile); - if (QFileInfo(filePath).exists()) - return filePath; - } - - return QString(); -} diff --git a/gui/checkthread.h b/gui/checkthread.h index 094d5c8ec..26000e3f5 100644 --- a/gui/checkthread.h +++ b/gui/checkthread.h @@ -57,10 +57,6 @@ public: mAddonsAndTools = addonsAndTools; } - void setMisraFile(const QString &misraFile) { - mMisraFile = misraFile; - } - void setDataDir(const QString &dataDir) { mDataDir = dataDir; } @@ -93,18 +89,6 @@ public: */ static QString clangTidyCmd(); - /** - * Determine command to run python - * \return Command to run python, empty if it is not found - */ - static QString pythonCmd(); - - /** - * Look for addon and return path - * \return path to addon if found, empty if it is not found - */ - static QString getAddonFilePath(const QString &dataDir, const QString &addonFile); - signals: /** @@ -144,7 +128,6 @@ protected: private: void runAddonsAndTools(const ImportProject::FileSettings *fileSettings, const QString &fileName); - void parseAddonErrors(QString err, const QString &tool); void parseClangErrors(const QString &tool, const QString &file0, QString err); bool isSuppressed(const Suppressions::ErrorMessage &errorMessage) const; @@ -155,7 +138,6 @@ private: QString mDataDir; QStringList mClangIncludePaths; QList mSuppressions; - QString mMisraFile; }; /// @} #endif // CHECKTHREAD_H diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 14fea3a2d..2f8dff4ff 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -451,7 +451,7 @@ void MainWindow::doAnalyzeProject(ImportProject p, const bool checkLibrary, cons //mThread->SetanalyzeProject(true); if (mProjectFile) { - mThread->setAddonsAndTools(mProjectFile->getAddonsAndTools(), mSettings->value(SETTINGS_MISRA_FILE).toString()); + mThread->setAddonsAndTools(mProjectFile->getAddonsAndTools()); QString clangHeaders = mSettings->value(SETTINGS_VS_INCLUDE_PATHS).toString(); mThread->setClangIncludePaths(clangHeaders.split(";")); mThread->setSuppressions(mProjectFile->getSuppressions()); @@ -494,7 +494,7 @@ void MainWindow::doAnalyzeFiles(const QStringList &files, const bool checkLibrar mThread->setFiles(fileNames); if (mProjectFile && !checkConfiguration) - mThread->setAddonsAndTools(mProjectFile->getAddonsAndTools(), mSettings->value(SETTINGS_MISRA_FILE).toString()); + mThread->setAddonsAndTools(mProjectFile->getAddonsAndTools()); mThread->setSuppressions(mProjectFile ? mProjectFile->getSuppressions() : QList()); QDir inf(mCurrentDirectory); const QString checkPath = inf.canonicalPath(); @@ -916,6 +916,30 @@ Settings MainWindow::getCppcheckSettings() result.safeChecks.externalVariables = mProjectFile->safeChecks.externalVariables; foreach (QString s, mProjectFile->getCheckUnknownFunctionReturn()) result.checkUnknownFunctionReturn.insert(s.toStdString()); + + QString filesDir(getDataDir(mSettings)); + const QString pythonCmd = mSettings->value(SETTINGS_PYTHON_PATH).toString(); + foreach (QString addon, mProjectFile->getAddons()) { + QString addonFilePath = ProjectFile::getAddonFilePath(filesDir, addon); + if (addonFilePath.isEmpty()) + continue; + + QString json; + json += "{ \"script\":\"" + addonFilePath + "\""; + if (!pythonCmd.isEmpty()) + json += ", \"python\":\"" + pythonCmd + "\""; + QString misraFile = mSettings->value(SETTINGS_MISRA_FILE).toString(); + if (addon == "misra" && !misraFile.isEmpty()) { + QString arg; + if (misraFile.endsWith(".pdf", Qt::CaseInsensitive)) + arg = "--misra-pdf=" + misraFile; + else + arg = "--rule-texts=" + misraFile; + json += ", \"args\":[\"" + arg + "\"]"; + } + json += " }"; + result.addons.push_back(json.toStdString()); + } } // Include directories (and files) are searched in listed order. @@ -1518,7 +1542,7 @@ void MainWindow::analyzeProject(const ProjectFile *projectFile, const bool check QDir::setCurrent(inf.absolutePath()); - mThread->setAddonsAndTools(projectFile->getAddonsAndTools(), mSettings->value(SETTINGS_MISRA_FILE).toString()); + mThread->setAddonsAndTools(projectFile->getAddonsAndTools()); mUI.mResults->setTags(projectFile->getTags()); // If the root path is not given or is not "current dir", use project diff --git a/gui/projectfile.cpp b/gui/projectfile.cpp index 13dd5b535..afb9fc006 100644 --- a/gui/projectfile.cpp +++ b/gui/projectfile.cpp @@ -995,3 +995,21 @@ void ProjectFile::SafeChecks::saveToXml(QXmlStreamWriter &xmlWriter) const } xmlWriter.writeEndElement(); } + +QString ProjectFile::getAddonFilePath(QString filesDir, QString addon) +{ + if (!filesDir.endsWith("/")) + filesDir += "/"; + + QStringList searchPaths; + searchPaths << filesDir << (filesDir + "addons/") << (filesDir + "../addons/"); + + foreach (QString path, searchPaths) { + QString f = path + addon + ".py"; + if (QFile(f).exists()) + return f; + } + + return QString(); +} + diff --git a/gui/projectfile.h b/gui/projectfile.h index 406e200f6..89d5bf7ac 100644 --- a/gui/projectfile.h +++ b/gui/projectfile.h @@ -167,6 +167,8 @@ public: return mAddons; } + static QString getAddonFilePath(QString filesDir, QString addon); + /** * @brief Get list of addons and tools. * @return list of addons and tools. diff --git a/gui/projectfiledialog.cpp b/gui/projectfiledialog.cpp index 059d537f0..ef18f8c65 100644 --- a/gui/projectfiledialog.cpp +++ b/gui/projectfiledialog.cpp @@ -234,7 +234,7 @@ static void updateAddonCheckBox(QCheckBox *cb, const ProjectFile *projectFile, c { if (projectFile) cb->setChecked(projectFile->getAddons().contains(addon)); - if (CheckThread::getAddonFilePath(dataDir, addon + ".py").isEmpty()) { + if (ProjectFile::getAddonFilePath(dataDir, addon).isEmpty()) { cb->setEnabled(false); cb->setText(cb->text() + QObject::tr(" (Not found)")); } diff --git a/gui/threadhandler.cpp b/gui/threadhandler.cpp index 3752f8a75..457904b43 100644 --- a/gui/threadhandler.cpp +++ b/gui/threadhandler.cpp @@ -94,7 +94,6 @@ void ThreadHandler::check(const Settings &settings) for (int i = 0; i < mRunningThreadCount; i++) { mThreads[i]->setAddonsAndTools(mAddonsAndTools); - mThreads[i]->setMisraFile(mMisraFile); mThreads[i]->setSuppressions(mSuppressions); mThreads[i]->setClangIncludePaths(mClangIncludePaths); mThreads[i]->setDataDir(mDataDir); diff --git a/gui/threadhandler.h b/gui/threadhandler.h index 8a0d86319..44c67155e 100644 --- a/gui/threadhandler.h +++ b/gui/threadhandler.h @@ -72,9 +72,8 @@ public: */ void saveSettings(QSettings &settings) const; - void setAddonsAndTools(const QStringList &addonsAndTools, const QString &misraFile) { + void setAddonsAndTools(const QStringList &addonsAndTools) { mAddonsAndTools = addonsAndTools; - mMisraFile = misraFile; } void setSuppressions(const QList &s) { @@ -262,7 +261,6 @@ protected: QStringList mClangIncludePaths; QString mDataDir; - QString mMisraFile; private: /** diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index 75744bb9d..076f6b0bd 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -87,7 +87,41 @@ namespace { return ""; } + std::string parseAddonInfo(const picojson::value &json, const std::string &fileName, const std::string &exename) { + std::string json_error = picojson::get_last_error(); + if (!json_error.empty()) { + return "Loading " + fileName + " failed. " + json_error; + } + if (!json.is()) + return "Loading " + fileName + " failed. Bad json."; + picojson::object obj = json.get(); + if (obj.count("args")) { + if (!obj["args"].is()) + return "Loading " + fileName + " failed. args must be array."; + for (const picojson::value &v : obj["args"].get()) + args += " " + v.get(); + } + + if (obj.count("python")) { + // Python was defined in the config file + if (obj["python"].is()) { + return "Loading " + fileName +" failed. python must not be an array."; + } + python = obj["python"].get(); + } else { + python = ""; + } + + return getAddonInfo(obj["script"].get(), exename); + } + std::string getAddonInfo(const std::string &fileName, const std::string &exename) { + if (fileName[0] == '{') { + std::istringstream in(fileName); + picojson::value json; + in >> json; + return parseAddonInfo(json, fileName, exename); + } if (fileName.find(".") == std::string::npos) return getAddonInfo(fileName + ".py", exename); @@ -117,31 +151,7 @@ namespace { return "Failed to open " + fileName; picojson::value json; fin >> json; - std::string json_error = picojson::get_last_error(); - if (!json_error.empty()) { - return "Loading " + fileName + " failed. " + json_error; - } - if (!json.is()) - return "Loading " + fileName + " failed. Bad json."; - picojson::object obj = json.get(); - if (obj.count("args")) { - if (!obj["args"].is()) - return "Loading " + fileName + " failed. args must be array."; - for (const picojson::value &v : obj["args"].get()) - args += " " + v.get(); - } - - if (obj.count("python")) { - // Python was defined in the config file - if (obj["python"].is()) { - return "Loading " + fileName +" failed. python must not be an array."; - } - python = obj["python"].get(); - } else { - python = ""; - } - - return getAddonInfo(obj["script"].get(), exename); + return parseAddonInfo(json, fileName, exename); } }; } diff --git a/lib/settings.h b/lib/settings.h index 77d77e8f2..e5fff6716 100644 --- a/lib/settings.h +++ b/lib/settings.h @@ -71,6 +71,7 @@ private: public: Settings(); + /** @brief addons, either filename of python/json file or json data */ std::list addons; /** @brief Path to the python interpreter to be used to run addons. */