diff --git a/CMakeLists.txt b/CMakeLists.txt index b215ac5ded..24357ba946 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,84 +1,72 @@ project(kdevclangtidy) set(VERSION_MAJOR 0) set(VERSION_MINOR 0) set(VERSION_PATCH 1) # KDevplatform dependency version set(KDEVPLATFORM_VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}") cmake_minimum_required(VERSION 2.8.12) find_package (ECM 0.0.9 REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH}) include(ECMAddTests) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings) include(FeatureSummary) find_package(Qt5 REQUIRED Core Widgets Test) find_package(KF5 REQUIRED COMPONENTS IconThemes ItemModels ThreadWeaver TextEditor I18n) find_package(KDevPlatform ${KDEVPLATFORM_VERSION} REQUIRED) -# find clang-tidy executable and sets it in plugin.h.in -find_program(CLANG_TIDY_EXEC NAMES "clang-tidy") -if(NOT CLANG_TIDY_EXEC) - message(ERROR "clang-tidy program not found") -else() - configure_file("${PROJECT_SOURCE_DIR}/src/plugin/plugin.h.in" - "${PROJECT_SOURCE_DIR}/src/plugin/plugin.h" ) - - configure_file("${PROJECT_SOURCE_DIR}/res/clangtidyconfig.kcfg.in" - "${PROJECT_SOURCE_DIR}/res/clangtidyconfig.kcfg" ) -endif() - set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH}) include_directories( ${kdevclangtidy_BINARY_DIR} ${kdevclangtidy_SOURCE_DIR}/src ) set(kdevclangtidy_PART_SRCS src/debug.cpp src/job.cpp src/plugin.cpp src/config/configgroup.cpp src/config/perprojectconfigpage.cpp src/config/clangtidypreferences.cpp src/parsers/clangtidyparser.cpp src/parsers/replacementparser.cpp ) include (cmake/ClangFormatAll.cmake) ki18n_wrap_ui( kdevclangtidy_PART_SRCS ${kdevclangtidy_PART_UI} src/config/ui/clangtidysettings.ui src/config/ui/perprojectconfig.ui ) kconfig_add_kcfg_files(kdevclangtidy_PART_SRCS res/clangtidyconfig.kcfgc) kdevplatform_add_plugin(kdevclangtidy JSON res/kdevclangtidy.json SOURCES ${kdevclangtidy_PART_SRCS} ${kdevclangtidy_CONFIG_SRCS}) target_link_libraries(kdevclangtidy KDev::Interfaces KDev::Project KDev::Language KDev::OutputView KDev::Util KDev::Shell KF5::ConfigCore KF5::IconThemes KF5::KIOCore KF5::WidgetsAddons KF5::TextEditor Qt5::Network ) install(FILES res/kdevclangtidy.rc DESTINATION ${KXMLGUI_INSTALL_DIR}/kdevclangtidy) add_subdirectory(tests) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/res/clangtidyconfig.kcfg b/res/clangtidyconfig.kcfg index e0d0112816..9fb01ae63d 100644 --- a/res/clangtidyconfig.kcfg +++ b/res/clangtidyconfig.kcfg @@ -1,14 +1,14 @@ - file:///usr/bin/clang-tidy + The full path to the clang-tidy executable The full path to the clang-tidy executable. diff --git a/res/clangtidyconfig.kcfg.in b/res/clangtidyconfig.kcfg.in deleted file mode 100644 index 6f80342b5b..0000000000 --- a/res/clangtidyconfig.kcfg.in +++ /dev/null @@ -1,14 +0,0 @@ - - - - - file://@CLANG_TIDY_EXEC@ - - The full path to the clang-tidy executable - The full path to the clang-tidy executable. - - - diff --git a/src/config/clangtidypreferences.cpp b/src/config/clangtidypreferences.cpp index 091ab7d809..658c26eeca 100644 --- a/src/config/clangtidypreferences.cpp +++ b/src/config/clangtidypreferences.cpp @@ -1,72 +1,72 @@ /* * This file is part of KDevelop * * Copyright 2016 Carlos Nihelton * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #include "config/clangtidypreferences.h" #include "clangtidyconfig.h" #include "config/configgroup.h" #include "ui_clangtidysettings.h" #include using KDevelop::IPlugin; using ClangTidy::ConfigGroup; using KDevelop::ConfigPage; ClangtidyPreferences::ClangtidyPreferences(IPlugin* plugin, QWidget* parent) : ConfigPage(plugin, ClangtidySettings::self(), parent) { QVBoxLayout* layout = new QVBoxLayout(this); QWidget* widget = new QWidget(this); ui = new Ui::ClangtidySettings(); ui->setupUi(widget); layout->addWidget(widget); } ClangtidyPreferences::~ClangtidyPreferences() { delete ui; } ConfigPage::ConfigPageType ClangtidyPreferences::configPageType() const { return ConfigPage::AnalyzerConfigPage; } QString ClangtidyPreferences::name() const { return i18n("clang-tidy"); } QString ClangtidyPreferences::fullName() const { return i18n("Configure clang-tidy settings"); } QIcon ClangtidyPreferences::icon() const { return QIcon::fromTheme(QStringLiteral("dialog-ok")); } void ClangtidyPreferences::apply() { - ConfigGroup projConf = KSharedConfig::openConfig()->group("Clangtidy"); - projConf.writeEntry(ConfigGroup::ExecutablePath, ui->kcfg_clangtidyPath->text()); + ConfigGroup configGroup = KSharedConfig::openConfig()->group("Clangtidy"); + configGroup.writeEntry(ConfigGroup::ExecutablePath, ui->kcfg_clangtidyPath->text()); } diff --git a/src/plugin.cpp b/src/plugin.cpp index fcd90287a1..450e399964 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -1,293 +1,290 @@ /* * This file is part of KDevelop * * Copyright 2016 Carlos Nihelton * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #include "plugin.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "config/clangtidypreferences.h" #include "config/perprojectconfigpage.h" #include "job.h" using namespace KDevelop; K_PLUGIN_FACTORY_WITH_JSON(ClangtidyFactory, "res/kdevclangtidy.json", registerPlugin();) namespace ClangTidy { Plugin::Plugin(QObject* parent, const QVariantList& /*unused*/) : IPlugin("kdevclangtidy", parent) , m_model(new KDevelop::ProblemModel(parent)) { qCDebug(KDEV_CLANGTIDY) << "setting clangtidy rc file"; setXMLFile("kdevclangtidy.rc"); QAction* act_checkfile; act_checkfile = actionCollection()->addAction("clangtidy_file", this, SLOT(runClangtidyFile())); act_checkfile->setStatusTip(i18n("Launches clang-tidy for current file")); act_checkfile->setText(i18n("clang-tidy")); /* TODO: Uncomment this only when discover a safe way to run clang-tidy on the whole project. // QAction* act_check_all_files; // act_check_all_files = actionCollection()->addAction ( "clangtidy_all", this, SLOT ( runClangtidyAll() ) ); // act_check_all_files->setStatusTip ( i18n ( "Launches clangtidy for all translation " // "units of current project" ) ); // act_check_all_files->setText ( i18n ( "clang-tidy (all)" ) ); */ IExecutePlugin* iface = KDevelop::ICore::self() ->pluginController() ->pluginForExtension("org.kdevelop.IExecutePlugin") ->extension(); Q_ASSERT(iface); ProblemModelSet* pms = core()->languageController()->problemModelSet(); pms->addModel(QStringLiteral("Clangtidy"), i18n("Clang-Tidy"), m_model.data()); m_config = KSharedConfig::openConfig()->group("Clangtidy"); - auto clangtidyPath = m_config.readEntry(ConfigGroup::ExecutablePath); - - // TODO(cnihelton): auto detect clang-tidy executable instead of hard-coding - // it. - if (clangtidyPath.isEmpty()) { - clangtidyPath = QString(CLANG_TIDY_PATH); + auto clangTidyPath = m_config.readEntry(ConfigGroup::ExecutablePath); + if (clangTidyPath.isEmpty()) { + clangTidyPath = QStandardPaths::findExecutable("clang-tidy"); } - collectAllAvailableChecks(clangtidyPath); + collectAllAvailableChecks(clangTidyPath); m_config.writeEntry(ConfigGroup::AdditionalParameters, ""); for (auto check : m_allChecks) { bool enable = check.contains("cert") || check.contains("-core.") || check.contains("-cplusplus") || check.contains("-deadcode") || check.contains("-security") || check.contains("cppcoreguide"); if (enable) { m_activeChecks << check; } else { m_activeChecks.removeAll(check); } } m_activeChecks.removeDuplicates(); m_config.writeEntry(ConfigGroup::EnabledChecks, m_activeChecks.join(',')); } void Plugin::unload() { ProblemModelSet* pms = core()->languageController()->problemModelSet(); pms->removeModel(QStringLiteral("Clangtidy")); } -void Plugin::collectAllAvailableChecks(QString clangtidyPath) +void Plugin::collectAllAvailableChecks(QString clangTidyPath) { m_allChecks.clear(); KProcess tidy; - tidy << clangtidyPath << QLatin1String("-checks=*") << QLatin1String("--list-checks"); + tidy << clangTidyPath << QLatin1String("-checks=*") << QLatin1String("--list-checks"); tidy.setOutputChannelMode(KProcess::OnlyStdoutChannel); tidy.start(); if (!tidy.waitForStarted()) { qCDebug(KDEV_CLANGTIDY) << "Unable to execute clang-tidy."; return; } tidy.closeWriteChannel(); if (!tidy.waitForFinished()) { qCDebug(KDEV_CLANGTIDY) << "Failed during clang-tidy execution."; return; } QTextStream ios(&tidy); QString each; while (ios.readLineInto(&each)) { m_allChecks.append(each.trimmed()); } if (m_allChecks.size() > 3) { m_allChecks.removeAt(m_allChecks.length() - 1); m_allChecks.removeAt(0); } m_allChecks.removeDuplicates(); } void Plugin::runClangtidy(bool allFiles) { KDevelop::IDocument* doc = core()->documentController()->activeDocument(); if (doc == nullptr) { QMessageBox::critical(nullptr, i18n("Error starting clang-tidy"), i18n("No suitable active file, unable to deduce project.")); return; } KDevelop::IProject* project = core()->projectController()->findProjectForUrl(doc->url()); if (project == nullptr) { QMessageBox::critical(nullptr, i18n("Error starting clang-tidy"), i18n("Active file isn't in a project")); return; } m_config = project->projectConfiguration()->group("Clangtidy"); if (!m_config.isValid()) { QMessageBox::critical(nullptr, i18n("Error starting clang-tidy"), i18n("Can't load parameters. They must be set in the " "project settings.")); return; } - auto clangtidyPath = m_config.readEntry(ConfigGroup::ExecutablePath); + ConfigGroup configGroup = KSharedConfig::openConfig()->group("Clangtidy"); + auto clangTidyPath = configGroup.readEntry(ConfigGroup::ExecutablePath); auto buildSystem = project->buildSystemManager(); Job::Parameters params; params.projectRootDir = project->path().toLocalFile(); - // TODO: auto detect clang-tidy executable instead of hard-coding it. - if (clangtidyPath.isEmpty()) { - params.executablePath = QStringLiteral("/usr/bin/clang-tidy"); + if (clangTidyPath.isEmpty()) { + params.executablePath = QStandardPaths::findExecutable("/usr/bin/clang-tidy"); } else { - params.executablePath = clangtidyPath; + params.executablePath = clangTidyPath; } if (allFiles) { params.filePath = project->path().toUrl().toLocalFile(); } else { params.filePath = doc->url().toLocalFile(); } params.buildDir = buildSystem->buildDirectory(project->projectItem()).toLocalFile(); params.additionalParameters = m_config.readEntry(ConfigGroup::AdditionalParameters); params.analiseTempDtors = m_config.readEntry(ConfigGroup::AnaliseTempDtors); params.enabledChecks = m_activeChecks.join(','); params.useConfigFile = m_config.readEntry(ConfigGroup::UseConfigFile); params.dumpConfig = m_config.readEntry(ConfigGroup::DumpConfig); params.enableChecksProfile = m_config.readEntry(ConfigGroup::EnableChecksProfile); params.exportFixes = m_config.readEntry(ConfigGroup::ExportFixes); params.extraArgs = m_config.readEntry(ConfigGroup::ExtraArgs); params.extraArgsBefore = m_config.readEntry(ConfigGroup::ExtraArgsBefore); params.autoFix = m_config.readEntry(ConfigGroup::AutoFix); params.headerFilter = m_config.readEntry(ConfigGroup::HeaderFilter); params.lineFilter = m_config.readEntry(ConfigGroup::LineFilter); params.listChecks = m_config.readEntry(ConfigGroup::ListChecks); params.checkSystemHeaders = m_config.readEntry(ConfigGroup::CheckSystemHeaders); if (!params.dumpConfig.isEmpty()) { auto job = new ClangTidy::Job(params, this); core()->runController()->registerJob(job); params.dumpConfig = QString(); } auto job2 = new ClangTidy::Job(params, this); connect(job2, SIGNAL(finished(KJob*)), this, SLOT(result(KJob*))); core()->runController()->registerJob(job2); } void Plugin::runClangtidyFile() { bool allFiles = false; runClangtidy(allFiles); } void Plugin::runClangtidyAll() { bool allFiles = true; runClangtidy(allFiles); } void Plugin::loadOutput() { } void Plugin::result(KJob* job) { Job* aj = dynamic_cast(job); if (aj == nullptr) { return; } if (aj->status() == KDevelop::OutputExecuteJob::JobStatus::JobSucceeded) { m_model->setProblems(aj->problems()); core()->uiController()->findToolView(i18nd("kdevproblemreporter", "Problems"), nullptr, KDevelop::IUiController::FindFlags::Raise); } } KDevelop::ContextMenuExtension Plugin::contextMenuExtension(KDevelop::Context* context) { KDevelop::IDocument* doc = core()->documentController()->activeDocument(); KDevelop::ContextMenuExtension extension = KDevelop::IPlugin::contextMenuExtension(context); if (context->type() == KDevelop::Context::EditorContext) { auto mime = doc->mimeType().name(); if (mime == QLatin1String("text/x-c++src") || mime == QLatin1String("text/x-csrc")) { QAction* action = new QAction(QIcon::fromTheme("dialog-ok"), i18n("Check unit with clang-tidy"), this); connect(action, SIGNAL(triggered(bool)), this, SLOT(runClangtidyFile())); extension.addAction(KDevelop::ContextMenuExtension::ExtensionGroup, action); } } return extension; } KDevelop::ConfigPage* Plugin::perProjectConfigPage(int number, const ProjectConfigOptions& options, QWidget* parent) { if (number != 0) { return nullptr; } auto config = new PerProjectConfigPage(options.project, parent); config->setActiveChecksReceptorList(&m_activeChecks); config->setList(m_allChecks); return config; } KDevelop::ConfigPage* Plugin::configPage(int number, QWidget* parent) { if (number != 0) { return nullptr; } return new ClangtidyPreferences(this, parent); } } // namespace ClangTidy #include "plugin.moc" diff --git a/src/plugin.h.in b/src/plugin.h.in deleted file mode 100644 index 80cfe013cd..0000000000 --- a/src/plugin.h.in +++ /dev/null @@ -1,107 +0,0 @@ -/* - * This file is part of KDevelop - * - * Copyright 2016 Carlos Nihelton - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - */ - -#ifndef CLANGTIDY_PLUGIN_H -#define CLANGTIDY_PLUGIN_H - -#include -#include - -#include -#include -#include - -#include -#include -#include - -#include "config/configgroup.h" -#include "qCDebug/debug.h" - -class KJob; - -namespace KDevelop -{ -class ProblemModel; -} - -#define CLANG_TIDY_PATH "@CLANG_TIDY_EXEC@" - -namespace ClangTidy -{ -/** - * \class - * \brief implements the support for clang-tidy inside KDevelop by using the IPlugin interface. - */ -class Plugin : public KDevelop::IPlugin -{ - Q_OBJECT - -public: - Plugin(QObject* parent, const QVariantList& = QVariantList()); - ~Plugin() = default; - void unload() override; - KDevelop::ContextMenuExtension contextMenuExtension(KDevelop::Context* context) override; - int configPages() const override { return 1; } - /** - * \function - * \return the session configuration page for clang-tidy plugin. - */ - KDevelop::ConfigPage* configPage(int number, QWidget* parent) override; - int perProjectConfigPages() const override { return 1; } - /** - * \function - * \return the clang-tidy configuration page for the current project. - */ - KDevelop::ConfigPage* perProjectConfigPage(int number, const KDevelop::ProjectConfigOptions& options, - QWidget* parent) override; - /** - *\function - *\returns all available checks, obtained from clang-tidy program, ran with with "--checks=* --list-checks" - * parameters. - */ - QStringList allAvailableChecks() { return m_allChecks; } -protected: - /** - * \function - * \brief collects all available checks by running clang-tidy with the following parameters: "--checks=* - * --list-checks". - * \param clangtidyPath QString - the system path for the clang-tidy program. - */ - void collectAllAvailableChecks(QString clangtidyPath); - -private slots: - void loadOutput(); - void runClangtidy(bool allFiles); - void runClangtidyFile(); - void runClangtidyAll(); - void result(KJob* job); - -private: - ConfigGroup m_config; - QScopedPointer m_model; - QStringList m_allChecks; - QStringList m_activeChecks; -}; - -} // namespace ClangTidy - -#endif // CLANGTIDY_PLUGIN_H