diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 3744f520c58f..f8902d6792ea 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -486,6 +486,10 @@ void MainWindow::saveSettings() const void MainWindow::doAnalyzeProject(ImportProject p, const bool checkLibrary, const bool checkConfiguration) { + Settings checkSettings = getCppcheckSettings(); + if (Settings::terminated()) + return; + clearResults(); mIsLogfileLoaded = false; @@ -522,7 +526,6 @@ void MainWindow::doAnalyzeProject(ImportProject p, const bool checkLibrary, cons checkLockDownUI(); // lock UI while checking mUI->mResults->setCheckDirectory(checkPath); - Settings checkSettings = getCppcheckSettings(); checkSettings.force = false; checkSettings.checkLibrary = checkLibrary; checkSettings.checkConfiguration = checkConfiguration; @@ -550,9 +553,13 @@ void MainWindow::doAnalyzeProject(ImportProject p, const bool checkLibrary, cons void MainWindow::doAnalyzeFiles(const QStringList &files, const bool checkLibrary, const bool checkConfiguration) { - if (files.isEmpty()) { + if (files.isEmpty()) return; - } + + Settings checkSettings = getCppcheckSettings(); + if (Settings::terminated()) + return; + clearResults(); mIsLogfileLoaded = false; @@ -591,7 +598,6 @@ void MainWindow::doAnalyzeFiles(const QStringList &files, const bool checkLibrar checkLockDownUI(); // lock UI while checking mUI->mResults->setCheckDirectory(checkPath); - Settings checkSettings = getCppcheckSettings(); checkSettings.checkLibrary = checkLibrary; checkSettings.checkConfiguration = checkConfiguration; @@ -614,6 +620,10 @@ void MainWindow::doAnalyzeFiles(const QStringList &files, const bool checkLibrar void MainWindow::analyzeCode(const QString& code, const QString& filename) { + Settings checkSettings = getCppcheckSettings(); + if (Settings::terminated()) + return; + // Initialize dummy ThreadResult as ErrorLogger ThreadResult result; result.setFiles(QStringList(filename)); @@ -628,7 +638,7 @@ void MainWindow::analyzeCode(const QString& code, const QString& filename) // Create CppCheck instance CppCheck cppcheck(result, true, nullptr); - cppcheck.settings() = getCppcheckSettings(); + cppcheck.settings() = checkSettings; // Check checkLockDownUI(); @@ -905,13 +915,12 @@ bool MainWindow::tryLoadLibrary(Library *library, const QString& filename) return true; } -void MainWindow::loadAddon(Settings &settings, const QString &filesDir, const QString &pythonCmd, const QString& addon) +QString MainWindow::loadAddon(Settings &settings, const QString &filesDir, const QString &pythonCmd, const QString& addon) { - QString addonFilePath = ProjectFile::getAddonFilePath(filesDir, addon); - if (addonFilePath.isEmpty()) - return; // TODO: report an error + const QString addonFilePath = fromNativePath(ProjectFile::getAddonFilePath(filesDir, addon)); - addonFilePath.replace(QChar('\\'), QChar('/')); + if (addonFilePath.isEmpty()) + return tr("File not found: '%1'").arg(addon); picojson::object obj; obj["script"] = picojson::value(addonFilePath.toStdString()); @@ -929,15 +938,16 @@ void MainWindow::loadAddon(Settings &settings, const QString &filesDir, const QS obj["args"] = picojson::value(arg.toStdString()); } } - picojson::value json; - json.set(std::move(obj)); - std::string json_str = json.serialize(); + + const std::string& json_str = picojson::value(obj).serialize(); AddonInfo addonInfo; - addonInfo.getAddonInfo(json_str, settings.exename); // TODO: handle error + const std::string errmsg = addonInfo.getAddonInfo(json_str, settings.exename); + if (!errmsg.empty()) + return tr("Failed to load/setup addon %1: %2").arg(addon, QString::fromStdString(errmsg)); settings.addonInfos.emplace_back(std::move(addonInfo)); - settings.addons.emplace(std::move(json_str)); + settings.addons.emplace(json_str); } Settings MainWindow::getCppcheckSettings() @@ -945,26 +955,37 @@ Settings MainWindow::getCppcheckSettings() saveSettings(); // Save settings Settings result; + Settings::terminate(false); result.exename = QCoreApplication::applicationFilePath().toStdString(); const bool std = tryLoadLibrary(&result.library, "std.cfg"); if (!std) QMessageBox::critical(this, tr("Error"), tr("Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir= at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured.").arg("std.cfg")); + Settings::terminate(true); + return result; + } const QString filesDir(getDataDir()); const QString pythonCmd = fromNativePath(mSettings->value(SETTINGS_PYTHON_PATH).toString()); { const QString cfgErr = QString::fromStdString(result.loadCppcheckCfg()); - if (!cfgErr.isEmpty()) + if (!cfgErr.isEmpty()) { QMessageBox::critical(this, tr("Error"), tr("Failed to load %1 - %2").arg("cppcheck.cfg").arg(cfgErr)); + Settings::terminate(true); + return result; + } const auto cfgAddons = result.addons; result.addons.clear(); for (const std::string& addon : cfgAddons) { // TODO: support addons which are a script and not a file - loadAddon(result, filesDir, pythonCmd, QString::fromStdString(addon)); + const QString addonError = loadAddon(result, filesDir, pythonCmd, QString::fromStdString(addon)); + if (!addonError.isEmpty()) { + QMessageBox::critical(this, tr("Error"), addonError); + Settings::terminate(true); + } } } @@ -1045,7 +1066,11 @@ Settings MainWindow::getCppcheckSettings() result.checkUnknownFunctionReturn.insert(s.toStdString()); for (const QString& addon : mProjectFile->getAddons()) { - loadAddon(result, filesDir, pythonCmd, addon); + const QString addonError = loadAddon(result, filesDir, pythonCmd, addon); + if (!addonError.isEmpty()) { + QMessageBox::critical(this, tr("Error"), addonError); + Settings::terminate(true); + } } if (isCppcheckPremium()) { @@ -1097,8 +1122,6 @@ Settings MainWindow::getCppcheckSettings() result.jobs = 1; } - Settings::terminate(false); - return result; } @@ -1223,6 +1246,10 @@ void MainWindow::reAnalyzeSelected(const QStringList& files) if (mThread->isChecking()) return; + const Settings& checkSettings = getCppcheckSettings(); + if (Settings::terminated()) + return; + // Clear details, statistics and progress mUI->mResults->clear(false); for (int i = 0; i < files.size(); ++i) @@ -1242,7 +1269,6 @@ void MainWindow::reAnalyzeSelected(const QStringList& files) // considered in "Modified Files Check" performed after "Selected Files Check" // TODO: Should we store per file CheckStartTime? QDateTime saveCheckStartTime = mThread->getCheckStartTime(); - const Settings& checkSettings = getCppcheckSettings(); mThread->check(checkSettings); mUI->mResults->setCheckSettings(checkSettings); mThread->setCheckStartTime(saveCheckStartTime); @@ -1254,6 +1280,10 @@ void MainWindow::reAnalyze(bool all) if (files.empty()) return; + const Settings& checkSettings = getCppcheckSettings(); + if (Settings::terminated()) + return; + // Clear details, statistics and progress mUI->mResults->clear(all); @@ -1268,7 +1298,6 @@ void MainWindow::reAnalyze(bool all) qDebug() << "Rechecking project file" << mProjectFile->getFilename(); mThread->setCheckFiles(all); - const Settings& checkSettings = getCppcheckSettings(); mThread->check(checkSettings); mUI->mResults->setCheckSettings(checkSettings); } diff --git a/gui/mainwindow.h b/gui/mainwindow.h index 4b6b20f9ad31..a6395d5da748 100644 --- a/gui/mainwindow.h +++ b/gui/mainwindow.h @@ -401,7 +401,7 @@ private slots: */ bool tryLoadLibrary(Library *library, const QString& filename); - void loadAddon(Settings &settings, const QString &filesDir, const QString &pythonCmd, const QString& addon); + QString loadAddon(Settings &settings, const QString &filesDir, const QString &pythonCmd, const QString& addon); /** * @brief Update project MRU items in File-menu. diff --git a/gui/projectfile.cpp b/gui/projectfile.cpp index f24f6eece936..393063bb49da 100644 --- a/gui/projectfile.cpp +++ b/gui/projectfile.cpp @@ -1115,6 +1115,9 @@ void ProjectFile::SafeChecks::saveToXml(QXmlStreamWriter &xmlWriter) const QString ProjectFile::getAddonFilePath(QString filesDir, const QString &addon) { + if (QFile(addon).exists()) + return addon; + if (!filesDir.endsWith("/")) filesDir += "/"; diff --git a/gui/test/projectfile/testprojectfile.cpp b/gui/test/projectfile/testprojectfile.cpp index 6e5e73b0f063..505a6e8937c1 100644 --- a/gui/test/projectfile/testprojectfile.cpp +++ b/gui/test/projectfile/testprojectfile.cpp @@ -28,6 +28,7 @@ #include #include +#include #include // Mock... @@ -116,4 +117,23 @@ void TestProjectFile::loadSimpleNoroot() const QCOMPARE(defines[0], QString("FOO")); } +void TestProjectFile::getAddonFilePath() const +{ + QTemporaryDir tempdir; + QVERIFY(tempdir.isValid()); + const QString filepath(tempdir.path() + "/addon.py"); + + QFile file(filepath); + QVERIFY(file.open(QIODevice::WriteOnly | QIODevice::Text)); + file.close(); + + // Relative path to addon + QCOMPARE(ProjectFile::getAddonFilePath(tempdir.path(), "addon"), filepath); + QCOMPARE(ProjectFile::getAddonFilePath(tempdir.path(), "not exist"), QString()); + + // Absolute path to addon + QCOMPARE(ProjectFile::getAddonFilePath("/not/exist", filepath), filepath); + QCOMPARE(ProjectFile::getAddonFilePath(tempdir.path(), filepath), filepath); +} + QTEST_MAIN(TestProjectFile) diff --git a/gui/test/projectfile/testprojectfile.h b/gui/test/projectfile/testprojectfile.h index b0bc7d969691..47ed517f412a 100644 --- a/gui/test/projectfile/testprojectfile.h +++ b/gui/test/projectfile/testprojectfile.h @@ -27,4 +27,6 @@ private slots: void loadSimple() const; void loadSimpleWithIgnore() const; void loadSimpleNoroot() const; + + void getAddonFilePath() const; }; diff --git a/lib/addoninfo.cpp b/lib/addoninfo.cpp index 7930f2e591c1..8c07fff5b169 100644 --- a/lib/addoninfo.cpp +++ b/lib/addoninfo.cpp @@ -106,7 +106,7 @@ static std::string parseAddonInfo(AddonInfo& addoninfo, const picojson::value &j if (!val.is()) return "Loading " + fileName + " failed. 'executable' must be a string."; addoninfo.executable = getFullPath(val.get(), fileName); - return ""; // TODO: why bail out? + return ""; // <- do not load both "executable" and "script". } }