ClangTools: Allow to disable a specific type of diagnostic

... from the results list.
This is much more convenient for the user than having to locate the
diagnostic in the settings first.
This patch deals with the global settings. We plan to offer the same for
project-level settings in a follow-up patch.

Task-number: QTCREATORBUG-24852
Change-Id: I7a97189c393048b98b9c2cdb6f21861a34670e8f
Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
Christian Kandeler
2020-11-12 14:15:36 +01:00
parent c73f93a7cf
commit ab91e547da
6 changed files with 172 additions and 2 deletions

View File

@@ -27,13 +27,16 @@
#include "clangtoolsdiagnosticmodel.h"
#include "clangtoolsprojectsettings.h"
#include "clangtoolssettings.h"
#include "clangtoolsutils.h"
#include "diagnosticconfigswidget.h"
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/manhattanstyle.h>
#include <debugger/analyzer/diagnosticlocation.h>
#include <utils/algorithm.h>
#include <utils/fileutils.h>
#include <utils/qtcassert.h>
#include <utils/theme/theme.h>
@@ -45,7 +48,11 @@
#include <QHeaderView>
#include <QPainter>
#include <QSet>
#include <QUuid>
#include <set>
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<QString> handledNames;
const QModelIndexList indexes = selectionModel()->selectedRows();
for (const QModelIndex &index : indexes) {
const Diagnostic diag = model()->data(index, ClangToolsDiagnosticModel::DiagnosticRole)
.value<Diagnostic>();
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<QModelIndex> 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<QAction *> DiagnosticView::customActions() const
{
const QModelIndex currentIndex = selectionModel()->currentIndex();
@@ -292,6 +385,9 @@ QList<QAction *> 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<QAction *> DiagnosticView::customActions() const
m_filterOutCurrentKind,
m_separator2,
m_suppressAction,
m_disableGloballyAction
};
}

View File

@@ -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;

View File

@@ -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)

View File

@@ -57,6 +57,8 @@ QString createDiagnosticToolTipString(
Utils::optional<FixitStatus> status = Utils::nullopt,
bool showSteps = true);
CppTools::ClangDiagnosticConfig builtinConfig();
QString createFullLocationString(const Debugger::DiagnosticLocation &location);
QString hintAboutBuildBeforeAnalysis();

View File

@@ -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 &current, 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

View File

@@ -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;