diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 3744f520c58..2b360ee5d0c 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,18 @@ 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); + + return ""; } Settings MainWindow::getCppcheckSettings() @@ -945,26 +957,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")); + 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.\n\nAnalysis is aborted.").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()) - QMessageBox::critical(this, tr("Error"), tr("Failed to load %1 - %2").arg("cppcheck.cfg").arg(cfgErr)); + if (!cfgErr.isEmpty()) { + QMessageBox::critical(this, tr("Error"), tr("Failed to load %1 - %2\n\nAnalysis is aborted.").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"), tr("%1\n\nAnalysis is aborted.").arg(addonError)); + Settings::terminate(true); + } } } @@ -1045,7 +1068,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"), tr("%1\n\nAnalysis is aborted.").arg(addonError)); + Settings::terminate(true); + } } if (isCppcheckPremium()) { @@ -1097,8 +1124,6 @@ Settings MainWindow::getCppcheckSettings() result.jobs = 1; } - Settings::terminate(false); - return result; } @@ -1223,6 +1248,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 +1271,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 +1282,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 +1300,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 4b6b20f9ad3..a6395d5da74 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 f24f6eece93..393063bb49d 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 6e5e73b0f06..505a6e8937c 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 b0bc7d96969..47ed517f412 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 7930f2e591c..8c07fff5b16 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". } }