diff --git a/src/plugins/clangtools/clangtoolsdiagnosticview.cpp b/src/plugins/clangtools/clangtoolsdiagnosticview.cpp index 689886d3f26..de6122d80d1 100644 --- a/src/plugins/clangtools/clangtoolsdiagnosticview.cpp +++ b/src/plugins/clangtools/clangtoolsdiagnosticview.cpp @@ -27,13 +27,16 @@ #include "clangtoolsdiagnosticmodel.h" #include "clangtoolsprojectsettings.h" +#include "clangtoolssettings.h" #include "clangtoolsutils.h" +#include "diagnosticconfigswidget.h" #include #include #include +#include #include #include #include @@ -45,7 +48,11 @@ #include #include #include +#include +#include + +using namespace CppTools; using namespace Debugger; namespace ClangTools { @@ -166,6 +173,10 @@ DiagnosticView::DiagnosticView(QWidget *parent) connect(m_suppressAction, &QAction::triggered, this, &DiagnosticView::suppressCurrentDiagnostic); + m_disableGloballyAction = new QAction(this); + connect(m_disableGloballyAction, &QAction::triggered, + this, &DiagnosticView::disableCurrentDiagnosticGlobally); + installEventFilter(this); setStyle(m_style); @@ -228,6 +239,55 @@ void DiagnosticView::suppressCurrentDiagnostic() filterModel->addSuppressedDiagnostics(diags); } +void DiagnosticView::disableCurrentDiagnosticGlobally() +{ + ClangToolsSettings * const settings = ClangToolsSettings::instance(); + ClangDiagnosticConfigs configs = settings->diagnosticConfigs(); + ClangDiagnosticConfig config = Utils::findOrDefault(configs, + [settings](const ClangDiagnosticConfig &c) { + return c.id() == settings->runSettings().diagnosticConfigId(); + }); + const bool defaultWasActive = !config.id().isValid(); + if (defaultWasActive) { + QTC_ASSERT(configs.isEmpty(), return); + config = builtinConfig(); + config.setIsReadOnly(false); + config.setId(Utils::Id::fromString(QUuid::createUuid().toString())); + config.setDisplayName(tr("Custom Configuration")); + configs << config; + } + + std::set handledNames; + const QModelIndexList indexes = selectionModel()->selectedRows(); + for (const QModelIndex &index : indexes) { + const Diagnostic diag = model()->data(index, ClangToolsDiagnosticModel::DiagnosticRole) + .value(); + if (!diag.isValid()) + continue; + if (!handledNames.insert(diag.name).second) + continue; + + if (diag.name.startsWith("clazy-")) { + config.setClazyMode(ClangDiagnosticConfig::ClazyMode::UseCustomChecks); + config.setClazyChecks(removeClazyCheck(config.clazyChecks(), diag.name)); + } else if (config.clangTidyMode() != ClangDiagnosticConfig::TidyMode::UseConfigFile) { + config.setClangTidyMode(ClangDiagnosticConfig::TidyMode::UseCustomChecks); + config.setClangTidyChecks(removeClangTidyCheck(config.clangTidyChecks(), diag.name)); + } + } + + if (!defaultWasActive) { + for (ClangDiagnosticConfig &c : configs) { + if (c.id() == config.id()) { + c = config; + break; + } + } + } + settings->setDiagnosticConfigs(configs); + settings->writeSettings(); +} + void DiagnosticView::goNext() { const QModelIndex currentIndex = selectionModel()->currentIndex(); @@ -278,6 +338,39 @@ QModelIndex DiagnosticView::getTopLevelIndex(const QModelIndex &index, Direction return model()->index(row, 0); } +bool DiagnosticView::disableGloballyEnabled() const +{ + const QList indexes = selectionModel()->selectedIndexes(); + if (indexes.isEmpty()) + return false; + if (!Utils::anyOf(indexes, &QModelIndex::isValid)) + return false; + + ClangToolsSettings * const settings = ClangToolsSettings::instance(); + const ClangDiagnosticConfigs configs = settings->diagnosticConfigs(); + const ClangDiagnosticConfig activeConfig = Utils::findOrDefault(configs, + [settings](const ClangDiagnosticConfig &c) { + return c.id() == settings->runSettings().diagnosticConfigId(); + }); + + // If the user has not created any custom configuration yet, then we'll do that for + // them as an act of kindness. But if custom configurations exist and the default + // (read-only) one is active, then we don't offer the action, as it's not clear what + // exactly we should do there. + if (configs.isEmpty()) + return true; + if (!activeConfig.id().isValid()) + return false; + + // If all selected diagnostics come from clang-tidy and the active config is controlled + // by a .clang-tidy file, then we do not offer the action. + if (activeConfig.clangTidyMode() != ClangDiagnosticConfig::TidyMode::UseConfigFile) + return true; + return Utils::anyOf(indexes, [this](const QModelIndex &index) { + return model()->data(index).toString().startsWith("clazy-"); + }); +} + QList DiagnosticView::customActions() const { const QModelIndex currentIndex = selectionModel()->currentIndex(); @@ -292,6 +385,9 @@ QList DiagnosticView::customActions() const m_suppressAction->setEnabled(isDiagnosticItem || hasMultiSelection); m_suppressAction->setText(hasMultiSelection ? tr("Suppress Selected Diagnostics") : tr("Suppress This Diagnostic")); + m_disableGloballyAction->setEnabled(disableGloballyEnabled()); + m_disableGloballyAction->setText(hasMultiSelection ? tr("Disable Selected Diagnostics Globally") + : tr("Disable This Diagnostic Globally")); return { m_help, @@ -302,6 +398,7 @@ QList DiagnosticView::customActions() const m_filterOutCurrentKind, m_separator2, m_suppressAction, + m_disableGloballyAction }; } diff --git a/src/plugins/clangtools/clangtoolsdiagnosticview.h b/src/plugins/clangtools/clangtoolsdiagnosticview.h index 83a440c6071..e9e14c0efb2 100644 --- a/src/plugins/clangtools/clangtoolsdiagnosticview.h +++ b/src/plugins/clangtools/clangtoolsdiagnosticview.h @@ -61,11 +61,14 @@ private: void openEditorForCurrentIndex(); void suppressCurrentDiagnostic(); + void disableCurrentDiagnosticGlobally(); enum Direction { Next = 1, Previous = -1 }; QModelIndex getIndex(const QModelIndex &index, Direction direction) const; QModelIndex getTopLevelIndex(const QModelIndex &index, Direction direction) const; private: + bool disableGloballyEnabled() const; + QAction *m_help = nullptr; QAction *m_showFilter = nullptr; @@ -74,6 +77,7 @@ private: QAction *m_filterOutCurrentKind = nullptr; QAction *m_suppressAction = nullptr; + QAction *m_disableGloballyAction = nullptr; QAction *m_separator = nullptr; QAction *m_separator2 = nullptr; diff --git a/src/plugins/clangtools/clangtoolsutils.cpp b/src/plugins/clangtools/clangtoolsutils.cpp index 99cb1c9dd30..a33ec673dc1 100644 --- a/src/plugins/clangtools/clangtoolsutils.cpp +++ b/src/plugins/clangtools/clangtoolsutils.cpp @@ -256,6 +256,11 @@ QString clazyStandaloneExecutable() } static void addBuiltinConfigs(ClangDiagnosticConfigsModel &model) +{ + model.appendOrUpdate(builtinConfig()); +} + +ClangDiagnosticConfig builtinConfig() { ClangDiagnosticConfig config; config.setId(Constants::DIAG_CONFIG_TIDY_AND_CLAZY); @@ -265,8 +270,7 @@ static void addBuiltinConfigs(ClangDiagnosticConfigsModel &model) config.setClangOptions({"-w"}); // Do not emit any clang-only warnings config.setClangTidyMode(ClangDiagnosticConfig::TidyMode::UseDefaultChecks); config.setClazyMode(ClangDiagnosticConfig::ClazyMode::UseDefaultChecks); - - model.appendOrUpdate(config); + return config; } ClangDiagnosticConfigsModel diagnosticConfigsModel(const ClangDiagnosticConfigs &customConfigs) diff --git a/src/plugins/clangtools/clangtoolsutils.h b/src/plugins/clangtools/clangtoolsutils.h index 9b6ddfa4c26..7b49dd9e0f5 100644 --- a/src/plugins/clangtools/clangtoolsutils.h +++ b/src/plugins/clangtools/clangtoolsutils.h @@ -57,6 +57,8 @@ QString createDiagnosticToolTipString( Utils::optional status = Utils::nullopt, bool showSteps = true); +CppTools::ClangDiagnosticConfig builtinConfig(); + QString createFullLocationString(const Debugger::DiagnosticLocation &location); QString hintAboutBuildBeforeAnalysis(); diff --git a/src/plugins/clangtools/diagnosticconfigswidget.cpp b/src/plugins/clangtools/diagnosticconfigswidget.cpp index 9375e84c21f..700dba7f8cc 100644 --- a/src/plugins/clangtools/diagnosticconfigswidget.cpp +++ b/src/plugins/clangtools/diagnosticconfigswidget.cpp @@ -25,6 +25,9 @@ #include "diagnosticconfigswidget.h" +#include "clangtoolsutils.h" +#include "executableinfo.h" + #include "ui_clazychecks.h" #include "ui_tidychecks.h" @@ -266,8 +269,40 @@ public: } } + QModelIndex indexForName(const QString &name) const + { + return indexForName(QModelIndex(), name); + } + protected: bool m_enabled = true; + +private: + QModelIndex indexForName(const QModelIndex ¤t, const QString &name) const + { + QString nodeName; + QString remainingName = name; + if (current.isValid()) { + nodeName = data(current).toString(); + if (nodeName == name) + return current; + if (nodeName.endsWith('*')) + nodeName.chop(1); + if (!name.startsWith(nodeName)) { + if (!nodeName.contains("Level")) + return {}; + } else { + remainingName = name.mid(nodeName.length()); + } + } + const int childCount = rowCount(current); + for (int i = 0; i < childCount; ++i) { + const QModelIndex theIndex = indexForName(index(i, 0, current), remainingName); + if (theIndex.isValid()) + return theIndex; + } + return {}; + } }; static void openUrl(QAbstractItemModel *model, const QModelIndex &index) @@ -958,6 +993,30 @@ void DiagnosticConfigsWidget::syncClazyChecksGroupBox() m_clazyChecks->checksGroupBox->setTitle(title); } +QString removeClangTidyCheck(const QString &checks, const QString &check) +{ + const ClangTidyInfo tidyInfo(clangTidyExecutable()); + TidyChecksTreeModel model(tidyInfo.supportedChecks); + model.selectChecks(checks); + const QModelIndex index = model.indexForName(check); + if (!index.isValid()) + return checks; + model.setData(index, false, Qt::CheckStateRole); + return model.selectedChecks(); +} + +QString removeClazyCheck(const QString &checks, const QString &check) +{ + const ClazyStandaloneInfo clazyInfo(clazyStandaloneExecutable()); + ClazyChecksTreeModel model(clazyInfo.supportedChecks); + model.enableChecks(checks.split(',', Qt::SkipEmptyParts)); + const QModelIndex index = model.indexForName(check.mid(QString("clazy-").length())); + if (!index.isValid()) + return checks; + model.setData(index, false, Qt::CheckStateRole); + return model.enabledChecks().join(','); +} + } // namespace Internal } // namespace ClangTools diff --git a/src/plugins/clangtools/diagnosticconfigswidget.h b/src/plugins/clangtools/diagnosticconfigswidget.h index 251fdd0f472..fdf961df416 100644 --- a/src/plugins/clangtools/diagnosticconfigswidget.h +++ b/src/plugins/clangtools/diagnosticconfigswidget.h @@ -34,6 +34,10 @@ namespace ClangTools { namespace Internal { +// Not UI-related, but requires the tree model (or else a huge refactoring or code duplication). +QString removeClangTidyCheck(const QString &checks, const QString &check); +QString removeClazyCheck(const QString &checks, const QString &check); + namespace Ui { class ClazyChecks; class TidyChecks;