From 6e796591f445aa0f193b280b9c89fcf82fa2236d Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Thu, 19 Feb 2015 18:08:38 +0100 Subject: Allow users to suppress diagnostics. This patch deals with what is likely the most common use case: Filtering specific messages at a particular location. The current granularity is essentially per-file (and per-function, where possible), which seems more useful than taking line numbers into account, as that would not be robust with regards to code changes elsewhere in the file. We can fine-tune this if the need arises. Change-Id: I4e9b2671fa199339cc3b995953d072b840cd3205 Reviewed-by: Nikolai Kosjar --- .../clangstaticanalyzer/clangstaticanalyzer.pro | 9 +- .../clangstaticanalyzer/clangstaticanalyzer.qbs | 7 + .../clangstaticanalyzerdiagnosticmodel.cpp | 71 +++++++++ .../clangstaticanalyzerdiagnosticmodel.h | 27 ++++ .../clangstaticanalyzerdiagnosticview.cpp | 47 +++++- .../clangstaticanalyzerdiagnosticview.h | 9 ++ .../clangstaticanalyzerplugin.cpp | 8 ++ .../clangstaticanalyzerprojectsettings.cpp | 137 ++++++++++++++++++ .../clangstaticanalyzerprojectsettings.h | 87 +++++++++++ .../clangstaticanalyzerprojectsettingsmanager.cpp | 50 +++++++ .../clangstaticanalyzerprojectsettingsmanager.h | 47 ++++++ .../clangstaticanalyzerprojectsettingswidget.cpp | 160 +++++++++++++++++++++ .../clangstaticanalyzerprojectsettingswidget.h | 52 +++++++ .../clangstaticanalyzerprojectsettingswidget.ui | 87 +++++++++++ .../clangstaticanalyzertool.cpp | 18 ++- .../clangstaticanalyzer/clangstaticanalyzertool.h | 2 + 16 files changed, 808 insertions(+), 10 deletions(-) create mode 100644 plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettings.cpp create mode 100644 plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettings.h create mode 100644 plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingsmanager.cpp create mode 100644 plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingsmanager.h create mode 100644 plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingswidget.cpp create mode 100644 plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingswidget.h create mode 100644 plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingswidget.ui (limited to 'plugins') diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzer.pro b/plugins/clangstaticanalyzer/clangstaticanalyzer.pro index 38ddda3e64..67eb90ad41 100644 --- a/plugins/clangstaticanalyzer/clangstaticanalyzer.pro +++ b/plugins/clangstaticanalyzer/clangstaticanalyzer.pro @@ -12,6 +12,9 @@ SOURCES += \ clangstaticanalyzerlogfilereader.cpp \ clangstaticanalyzerpathchooser.cpp \ clangstaticanalyzerplugin.cpp \ + clangstaticanalyzerprojectsettings.cpp \ + clangstaticanalyzerprojectsettingsmanager.cpp \ + clangstaticanalyzerprojectsettingswidget.cpp \ clangstaticanalyzerruncontrol.cpp \ clangstaticanalyzerruncontrolfactory.cpp \ clangstaticanalyzerrunner.cpp \ @@ -29,6 +32,9 @@ HEADERS += \ clangstaticanalyzerlogfilereader.h \ clangstaticanalyzerpathchooser.h \ clangstaticanalyzerplugin.h \ + clangstaticanalyzerprojectsettings.h \ + clangstaticanalyzerprojectsettingsmanager.h \ + clangstaticanalyzerprojectsettingswidget.h \ clangstaticanalyzerruncontrolfactory.h \ clangstaticanalyzerruncontrol.h \ clangstaticanalyzerrunner.h \ @@ -37,7 +43,8 @@ HEADERS += \ clangstaticanalyzerutils.h FORMS += \ - clangstaticanalyzerconfigwidget.ui + clangstaticanalyzerconfigwidget.ui \ + clangstaticanalyzerprojectsettingswidget.ui equals(TEST, 1) { HEADERS += clangstaticanalyzerunittests.h diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzer.qbs b/plugins/clangstaticanalyzer/clangstaticanalyzer.qbs index 9113cc94c3..a97dc9f83c 100644 --- a/plugins/clangstaticanalyzer/clangstaticanalyzer.qbs +++ b/plugins/clangstaticanalyzer/clangstaticanalyzer.qbs @@ -32,6 +32,13 @@ QtcPlugin { "clangstaticanalyzerpathchooser.h", "clangstaticanalyzerplugin.cpp", "clangstaticanalyzerplugin.h", + "clangstaticanalyzerprojectsettings.cpp", + "clangstaticanalyzerprojectsettings.h", + "clangstaticanalyzerprojectsettingsmanager.cpp", + "clangstaticanalyzerprojectsettingsmanager.h", + "clangstaticanalyzerprojectsettingswidget.cpp", + "clangstaticanalyzerprojectsettingswidget.h", + "clangstaticanalyzerprojectsettingswidget.ui", "clangstaticanalyzerruncontrol.cpp", "clangstaticanalyzerruncontrol.h", "clangstaticanalyzerruncontrolfactory.cpp", diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticmodel.cpp b/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticmodel.cpp index 5f1d21fb9f..40f5924442 100644 --- a/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticmodel.cpp +++ b/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticmodel.cpp @@ -18,9 +18,15 @@ #include "clangstaticanalyzerdiagnosticmodel.h" +#include "clangstaticanalyzerprojectsettingsmanager.h" #include "clangstaticanalyzerutils.h" +#include +#include +#include + #include +#include namespace ClangStaticAnalyzer { namespace Internal { @@ -118,5 +124,70 @@ QVariant ClangStaticAnalyzerDiagnosticModel::data(const QModelIndex &index, int return QVariant(); } + +ClangStaticAnalyzerDiagnosticFilterModel::ClangStaticAnalyzerDiagnosticFilterModel(QObject *parent) + : QSortFilterProxyModel(parent) +{ + // So that when a user closes and re-opens a project and *then* clicks "Suppress", + // we enter that information into the project settings. + connect(ProjectExplorer::SessionManager::instance(), + &ProjectExplorer::SessionManager::projectAdded, this, + [this](ProjectExplorer::Project *project) { + if (!m_project && project->projectDirectory() == m_lastProjectDirectory) + setProject(project); + }); +} + +void ClangStaticAnalyzerDiagnosticFilterModel::setProject(ProjectExplorer::Project *project) +{ + QTC_ASSERT(project, return); + if (m_project) { + disconnect(ProjectSettingsManager::getSettings(m_project), + &ProjectSettings::suppressedDiagnosticsChanged, this, + &ClangStaticAnalyzerDiagnosticFilterModel::handleSuppressedDiagnosticsChanged); + } + m_project = project; + m_lastProjectDirectory = m_project->projectDirectory(); + connect(ProjectSettingsManager::getSettings(m_project), + &ProjectSettings::suppressedDiagnosticsChanged, + this, &ClangStaticAnalyzerDiagnosticFilterModel::handleSuppressedDiagnosticsChanged); + handleSuppressedDiagnosticsChanged(); +} + +void ClangStaticAnalyzerDiagnosticFilterModel::addSuppressedDiagnostic( + const SuppressedDiagnostic &diag) +{ + QTC_ASSERT(!m_project, return); + m_suppressedDiagnostics << diag; + invalidate(); +} + +bool ClangStaticAnalyzerDiagnosticFilterModel::filterAcceptsRow(int sourceRow, + const QModelIndex &sourceParent) const +{ + Q_UNUSED(sourceParent); + const Diagnostic diag = static_cast(sourceModel()) + ->diagnostics().at(sourceRow); + foreach (const SuppressedDiagnostic &d, m_suppressedDiagnostics) { + if (d.description != diag.description) + continue; + QString filePath = d.filePath.toString(); + QFileInfo fi(filePath); + if (fi.isRelative()) + filePath = m_lastProjectDirectory.toString() + QLatin1Char('/') + filePath; + if (filePath == diag.location.filePath) + return false; + } + return true; +} + +void ClangStaticAnalyzerDiagnosticFilterModel::handleSuppressedDiagnosticsChanged() +{ + QTC_ASSERT(m_project, return); + m_suppressedDiagnostics + = ProjectSettingsManager::getSettings(m_project)->suppressedDiagnostics(); + invalidate(); +} + } // namespace Internal } // namespace ClangStaticAnalyzer diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticmodel.h b/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticmodel.h index 68dcf7ed05..a2dc49c26c 100644 --- a/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticmodel.h +++ b/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticmodel.h @@ -20,8 +20,15 @@ #define CLANGSTATICANALYZERDIAGNOSTICMODEL_H #include "clangstaticanalyzerlogfilereader.h" +#include "clangstaticanalyzerprojectsettings.h" + +#include #include +#include +#include + +namespace ProjectExplorer { class Project; } namespace ClangStaticAnalyzer { namespace Internal { @@ -45,6 +52,26 @@ private: QList m_diagnostics; }; +class ClangStaticAnalyzerDiagnosticFilterModel : public QSortFilterProxyModel +{ + Q_OBJECT + +public: + ClangStaticAnalyzerDiagnosticFilterModel(QObject *parent = 0); + + void setProject(ProjectExplorer::Project *project); + void addSuppressedDiagnostic(const SuppressedDiagnostic &diag); + ProjectExplorer::Project *project() const { return m_project; } + +private: + bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const; + void handleSuppressedDiagnosticsChanged(); + + QPointer m_project; + Utils::FileName m_lastProjectDirectory; + SuppressedDiagnosticsList m_suppressedDiagnostics; +}; + } // namespace Internal } // namespace ClangStaticAnalyzer diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticview.cpp b/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticview.cpp index 54c8ef5a53..f1805f2da2 100644 --- a/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticview.cpp +++ b/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticview.cpp @@ -19,10 +19,15 @@ #include "clangstaticanalyzerdiagnosticview.h" #include "clangstaticanalyzerlogfilereader.h" +#include "clangstaticanalyzerdiagnosticmodel.h" +#include "clangstaticanalyzerprojectsettings.h" +#include "clangstaticanalyzerprojectsettingsmanager.h" #include "clangstaticanalyzerutils.h" +#include #include +#include #include #include #include @@ -196,13 +201,18 @@ DetailedErrorDelegate::SummaryLineInfo ClangStaticAnalyzerDiagnosticDelegate::su return info; } +Diagnostic ClangStaticAnalyzerDiagnosticDelegate::getDiagnostic(const QModelIndex &index) const +{ + return index.data(Qt::UserRole).value(); +} + QWidget *ClangStaticAnalyzerDiagnosticDelegate::createDetailsWidget(const QFont &font, const QModelIndex &index, QWidget *parent) const { QWidget *widget = new QWidget(parent); - const Diagnostic diagnostic = index.data(Qt::UserRole).value(); + const Diagnostic diagnostic = getDiagnostic(index); if (!diagnostic.isValid()) return widget; @@ -240,7 +250,7 @@ QString ClangStaticAnalyzerDiagnosticDelegate::textualRepresentation() const { QTC_ASSERT(m_detailsIndex.isValid(), return QString()); - const Diagnostic diagnostic = m_detailsIndex.data(Qt::UserRole).value(); + const Diagnostic diagnostic = getDiagnostic(m_detailsIndex); QTC_ASSERT(diagnostic.isValid(), return QString()); // Create summary @@ -268,6 +278,39 @@ ClangStaticAnalyzerDiagnosticView::ClangStaticAnalyzerDiagnosticView(QWidget *pa ClangStaticAnalyzerDiagnosticDelegate *delegate = new ClangStaticAnalyzerDiagnosticDelegate(this); setItemDelegate(delegate); + m_suppressAction = new QAction(tr("Suppress this diagnostic"), this); + connect(m_suppressAction, &QAction::triggered, [this](bool) { suppressCurrentDiagnostic(); }); +} + +void ClangStaticAnalyzerDiagnosticView::suppressCurrentDiagnostic() +{ + const QModelIndexList indexes = selectedIndexes(); + QTC_ASSERT(indexes.count() == 1, return); + const Diagnostic diag = static_cast(itemDelegate()) + ->getDiagnostic(indexes.first()); + QTC_ASSERT(diag.isValid(), return); + + // If the original project was closed, we work directly on the filter model, otherwise + // we go via the project settings. + auto * const filterModel = static_cast(model()); + ProjectExplorer::Project * const project = filterModel->project(); + if (project) { + Utils::FileName filePath = Utils::FileName::fromString(diag.location.filePath); + const Utils::FileName relativeFilePath + = filePath.relativeChildPath(project->projectDirectory()); + if (!relativeFilePath.isEmpty()) + filePath = relativeFilePath; + const SuppressedDiagnostic supDiag(filePath, diag.description, diag.issueContextKind, + diag.issueContext, diag.explainingSteps.count()); + ProjectSettingsManager::getSettings(project)->addSuppressedDiagnostic(supDiag); + } else { + filterModel->addSuppressedDiagnostic(SuppressedDiagnostic(diag)); + } +} + +QList ClangStaticAnalyzerDiagnosticView::customActions() const +{ + return QList() << m_suppressAction; } } // namespace Internal diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticview.h b/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticview.h index ed6acae4f3..4fbdc7f866 100644 --- a/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticview.h +++ b/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticview.h @@ -23,6 +23,7 @@ namespace ClangStaticAnalyzer { namespace Internal { +class Diagnostic; class ClangStaticAnalyzerDiagnosticView : public Analyzer::DetailedErrorView { @@ -30,6 +31,13 @@ class ClangStaticAnalyzerDiagnosticView : public Analyzer::DetailedErrorView public: ClangStaticAnalyzerDiagnosticView(QWidget *parent = 0); + +private: + void suppressCurrentDiagnostic(); + + QList customActions() const; + + QAction *m_suppressAction; }; class ClangStaticAnalyzerDiagnosticDelegate : public Analyzer::DetailedErrorDelegate @@ -38,6 +46,7 @@ public: ClangStaticAnalyzerDiagnosticDelegate(QListView *parent); SummaryLineInfo summaryInfo(const QModelIndex &index) const; + Diagnostic getDiagnostic(const QModelIndex &index) const; private: QWidget *createDetailsWidget(const QFont &font, const QModelIndex &index, diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerplugin.cpp b/plugins/clangstaticanalyzer/clangstaticanalyzerplugin.cpp index 52673def09..dced8b03d6 100644 --- a/plugins/clangstaticanalyzer/clangstaticanalyzerplugin.cpp +++ b/plugins/clangstaticanalyzer/clangstaticanalyzerplugin.cpp @@ -19,6 +19,7 @@ #include "clangstaticanalyzerplugin.h" #include "clangstaticanalyzerconfigwidget.h" +#include "clangstaticanalyzerprojectsettingswidget.h" #include "clangstaticanalyzerruncontrolfactory.h" #include "clangstaticanalyzertool.h" @@ -35,6 +36,7 @@ #include #include #include +#include #include @@ -106,6 +108,12 @@ bool ClangStaticAnalyzerPlugin::initialize(const QStringList &arguments, QString // In the initialize method, a plugin can be sure that the plugins it // depends on have initialized their members. + auto panelFactory = new ProjectExplorer::ProjectPanelFactory(); + panelFactory->setPriority(100); + panelFactory->setDisplayName(tr("Clang Static Analyzer Settings")); + panelFactory->setSimpleCreateWidgetFunction(QIcon()); + ProjectExplorer::ProjectPanelFactory::registerFactory(panelFactory); + LicenseChecker::LicenseCheckerPlugin *licenseChecker = ExtensionSystem::PluginManager::getObject(); diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettings.cpp b/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettings.cpp new file mode 100644 index 0000000000..7ee14816be --- /dev/null +++ b/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettings.cpp @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us +** +** This file is part of the Qt Enterprise Qt Quick Profiler Add-on. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. +** +** If you have questions regarding the use of this file, please use +** contact form at http://www.qt.io/contact-us +** +****************************************************************************/ +#include "clangstaticanalyzerprojectsettings.h" + +#include "clangstaticanalyzerdiagnostic.h" + +#include + +namespace ClangStaticAnalyzer { +namespace Internal { + +static QString suppressedDiagnosticsKey() +{ + return QLatin1String("ClangStaticAnalyzer.SuppressedDiagnostics"); +} + +static QString suppressedDiagnosticFilePathKey() +{ + return QLatin1String("ClangStaticAnalyzer.SuppressedDiagnosticFilePath"); +} + +static QString suppressedDiagnosticMessageKey() +{ + return QLatin1String("ClangStaticAnalyzer.SuppressedDiagnosticMessage"); +} + +static QString suppressedDiagnosticContextKindKey() +{ + return QLatin1String("ClangStaticAnalyzer.SuppressedDiagnosticContextKind"); +} + +static QString suppressedDiagnosticContextKey() +{ + return QLatin1String("ClangStaticAnalyzer.SuppressedDiagnosticContext"); +} + +static QString suppressedDiagnosticUniquifierKey() +{ + return QLatin1String("ClangStaticAnalyzer.SuppressedDiagnosticUniquifier"); +} + +ProjectSettings::ProjectSettings(ProjectExplorer::Project *project) : m_project(project) +{ + load(); + connect(project, &ProjectExplorer::Project::aboutToSaveSettings, this, + &ProjectSettings::store); +} + +void ProjectSettings::addSuppressedDiagnostic(const SuppressedDiagnostic &diag) +{ + QTC_ASSERT(!m_suppressedDiagnostics.contains(diag), return); + m_suppressedDiagnostics << diag; + emit suppressedDiagnosticsChanged(); +} + +void ProjectSettings::removeSuppressedDiagnostic(const SuppressedDiagnostic &diag) +{ + const bool wasPresent = m_suppressedDiagnostics.removeOne(diag); + QTC_ASSERT(wasPresent, return); + emit suppressedDiagnosticsChanged(); +} + +void ProjectSettings::removeAllSuppressedDiagnostics() +{ + m_suppressedDiagnostics.clear(); + emit suppressedDiagnosticsChanged(); +} + +void ProjectSettings::load() +{ + const QVariantList list = m_project->namedSettings(suppressedDiagnosticsKey()).toList(); + foreach (const QVariant &v, list) { + const QVariantMap diag = v.toMap(); + const QString fp = diag.value(suppressedDiagnosticFilePathKey()).toString(); + if (fp.isEmpty()) + continue; + const QString message = diag.value(suppressedDiagnosticMessageKey()).toString(); + if (message.isEmpty()) + continue; + Utils::FileName fullPath = Utils::FileName::fromString(fp); + if (fullPath.toFileInfo().isRelative()) { + fullPath = m_project->projectDirectory(); + fullPath.appendPath(fp); + } + if (!fullPath.exists()) + continue; + const QString contextKind = diag.value(suppressedDiagnosticContextKindKey()).toString(); + const QString context = diag.value(suppressedDiagnosticContextKey()).toString(); + const int uniquifier = diag.value(suppressedDiagnosticUniquifierKey()).toInt(); + m_suppressedDiagnostics << SuppressedDiagnostic(Utils::FileName::fromString(fp), message, + contextKind, context, uniquifier); + } + emit suppressedDiagnosticsChanged(); +} + +void ProjectSettings::store() +{ + QVariantList list; + foreach (const SuppressedDiagnostic &diag, m_suppressedDiagnostics) { + QVariantMap diagMap; + diagMap.insert(suppressedDiagnosticFilePathKey(), diag.filePath.toString()); + diagMap.insert(suppressedDiagnosticMessageKey(), diag.description); + diagMap.insert(suppressedDiagnosticContextKindKey(), diag.contextKind); + diagMap.insert(suppressedDiagnosticContextKey(), diag.context); + diagMap.insert(suppressedDiagnosticUniquifierKey(), diag.uniquifier); + list << diagMap; + } + m_project->setNamedSettings(suppressedDiagnosticsKey(), list); +} + + +SuppressedDiagnostic::SuppressedDiagnostic(const Diagnostic &diag) + : filePath(Utils::FileName::fromString(diag.location.filePath)) + , description(diag.description) + , contextKind(diag.issueContextKind) + , context(diag.issueContext) + , uniquifier(diag.explainingSteps.count()) +{ +} + +} // namespace Internal +} // namespace ClangStaticAnalyzer diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettings.h b/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettings.h new file mode 100644 index 0000000000..2a993de966 --- /dev/null +++ b/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettings.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us +** +** This file is part of the Qt Enterprise Qt Quick Profiler Add-on. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. +** +** If you have questions regarding the use of this file, please use +** contact form at http://www.qt.io/contact-us +** +****************************************************************************/ +#ifndef CLANGSTATICANALYZERPROJECTSETTINGS_H +#define CLANGSTATICANALYZERPROJECTSETTINGS_H + +#include +#include + +#include +#include + +namespace ClangStaticAnalyzer { +namespace Internal { +class Diagnostic; + +class SuppressedDiagnostic +{ +public: + SuppressedDiagnostic(const Utils::FileName &filePath, const QString &description, + const QString &contextKind, const QString &context, int uniquifier) + : filePath(filePath) + , description(description) + , contextKind(contextKind) + , context(context) + , uniquifier(uniquifier) + { + } + + SuppressedDiagnostic(const Diagnostic &diag); + + Utils::FileName filePath; // Relative for files in project, absolute otherwise. + QString description; + QString contextKind; + QString context; + int uniquifier; +}; + +inline bool operator==(const SuppressedDiagnostic &d1, const SuppressedDiagnostic &d2) +{ + return d1.filePath == d2.filePath && d1.description == d2.description + && d1.contextKind == d2.contextKind && d1.context == d2.context + && d1.uniquifier == d2.uniquifier; +} + +typedef QList SuppressedDiagnosticsList; + +class ProjectSettings : public QObject +{ + Q_OBJECT +public: + ProjectSettings(ProjectExplorer::Project *project); + + SuppressedDiagnosticsList suppressedDiagnostics() const { return m_suppressedDiagnostics; } + void addSuppressedDiagnostic(const SuppressedDiagnostic &diag); + void removeSuppressedDiagnostic(const SuppressedDiagnostic &diag); + void removeAllSuppressedDiagnostics(); + +signals: + void suppressedDiagnosticsChanged(); + +private: + void load(); + void store(); + + ProjectExplorer::Project * const m_project; + SuppressedDiagnosticsList m_suppressedDiagnostics; +}; + +} // namespace Internal +} // namespace ClangStaticAnalyzer + +#endif // Include guard. diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingsmanager.cpp b/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingsmanager.cpp new file mode 100644 index 0000000000..8b1637bba1 --- /dev/null +++ b/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingsmanager.cpp @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us +** +** This file is part of the Qt Enterprise Qt Quick Profiler Add-on. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. +** +** If you have questions regarding the use of this file, please use +** contact form at http://www.qt.io/contact-us +** +****************************************************************************/ +#include "clangstaticanalyzerprojectsettingsmanager.h" + +#include "clangstaticanalyzerprojectsettings.h" + +#include + +namespace ClangStaticAnalyzer { +namespace Internal { + +ProjectSettingsManager::ProjectSettingsManager() +{ + QObject::connect(ProjectExplorer::SessionManager::instance(), + &ProjectExplorer::SessionManager::aboutToRemoveProject, + &ProjectSettingsManager::handleProjectToBeRemoved); +} + +ProjectSettings *ProjectSettingsManager::getSettings(ProjectExplorer::Project *project) +{ + auto &settings = m_settings[project]; + if (!settings) + settings.reset(new ProjectSettings(project)); + return settings.data(); +} + +void ProjectSettingsManager::handleProjectToBeRemoved(ProjectExplorer::Project *project) +{ + m_settings.remove(project); +} + +ProjectSettingsManager::SettingsMap ProjectSettingsManager::m_settings; + +} // namespace Internal +} // namespace ClangStaticAnalyzer diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingsmanager.h b/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingsmanager.h new file mode 100644 index 0000000000..97f8d79a99 --- /dev/null +++ b/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingsmanager.h @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us +** +** This file is part of the Qt Enterprise Qt Quick Profiler Add-on. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. +** +** If you have questions regarding the use of this file, please use +** contact form at http://www.qt.io/contact-us +** +****************************************************************************/ +#ifndef CLANGSTATICANALYZERPROJECTSETTINGSMANAGER_H +#define CLANGSTATICANALYZERPROJECTSETTINGSMANAGER_H + +namespace ProjectExplorer { class Project; } + +#include +#include + +namespace ClangStaticAnalyzer { +namespace Internal { +class ProjectSettings; + +class ProjectSettingsManager +{ +public: + ProjectSettingsManager(); + + static ProjectSettings *getSettings(ProjectExplorer::Project *project); + +private: + static void handleProjectToBeRemoved(ProjectExplorer::Project *project); + + typedef QHash> SettingsMap; + static SettingsMap m_settings; +}; + +} // namespace Internal +} // namespace ClangStaticAnalyzer + +#endif // Include guard. diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingswidget.cpp b/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingswidget.cpp new file mode 100644 index 0000000000..580e6f750d --- /dev/null +++ b/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingswidget.cpp @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us +** +** This file is part of the Qt Enterprise Qt Quick Profiler Add-on. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. +** +** If you have questions regarding the use of this file, please use +** contact form at http://www.qt.io/contact-us +** +****************************************************************************/ +#include "clangstaticanalyzerprojectsettingswidget.h" +#include "ui_clangstaticanalyzerprojectsettingswidget.h" + +#include "clangstaticanalyzerprojectsettings.h" +#include "clangstaticanalyzerprojectsettingsmanager.h" + +#include + +#include + +namespace ClangStaticAnalyzer { +namespace Internal { + +class SuppressedDiagnosticsModel : public QAbstractTableModel +{ + Q_OBJECT +public: + SuppressedDiagnosticsModel(QObject *parent = 0) : QAbstractTableModel(parent) { } + + void setDiagnostics(const SuppressedDiagnosticsList &diagnostics); + SuppressedDiagnostic diagnosticAt(int i) const; + +private: + enum Columns { ColumnFile, ColumnContext, ColumnDescription, ColumnLast = ColumnDescription }; + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex & = QModelIndex()) const { return ColumnLast + 1; } + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + + SuppressedDiagnosticsList m_diagnostics; +}; + +ProjectSettingsWidget::ProjectSettingsWidget(ProjectExplorer::Project *project, QWidget *parent) : + QWidget(parent), + m_ui(new Ui::ProjectSettingsWidget) + , m_projectSettings(ProjectSettingsManager::getSettings(project)) +{ + m_ui->setupUi(this); + auto * const model = new SuppressedDiagnosticsModel(this); + model->setDiagnostics(m_projectSettings->suppressedDiagnostics()); + connect(m_projectSettings, &ProjectSettings::suppressedDiagnosticsChanged, + [model, this] { + model->setDiagnostics(m_projectSettings->suppressedDiagnostics()); + updateButtonStates(); + }); + m_ui->diagnosticsView->setModel(model); + updateButtonStates(); + connect(m_ui->diagnosticsView->selectionModel(), &QItemSelectionModel::selectionChanged, + [this](const QItemSelection &, const QItemSelection &) { + updateButtonStateRemoveSelected(); + }); + connect(m_ui->removeSelectedButton, &QAbstractButton::clicked, + [this](bool) { removeSelected(); }); + connect(m_ui->removeAllButton, &QAbstractButton::clicked, + [this](bool) { m_projectSettings->removeAllSuppressedDiagnostics();}); +} + +ProjectSettingsWidget::~ProjectSettingsWidget() +{ + delete m_ui; +} + +void ProjectSettingsWidget::updateButtonStates() +{ + updateButtonStateRemoveSelected(); + updateButtonStateRemoveAll(); +} + +void ProjectSettingsWidget::updateButtonStateRemoveSelected() +{ + const auto selectedRows = m_ui->diagnosticsView->selectionModel()->selectedRows(); + QTC_ASSERT(selectedRows.count() <= 1, return); + m_ui->removeSelectedButton->setEnabled(!selectedRows.isEmpty()); +} + +void ProjectSettingsWidget::updateButtonStateRemoveAll() +{ + m_ui->removeAllButton->setEnabled(m_ui->diagnosticsView->model()->rowCount() > 0); +} + +void ProjectSettingsWidget::removeSelected() +{ + const auto selectedRows = m_ui->diagnosticsView->selectionModel()->selectedRows(); + QTC_ASSERT(selectedRows.count() == 1, return); + const auto * const model + = static_cast(m_ui->diagnosticsView->model()); + m_projectSettings->removeSuppressedDiagnostic(model->diagnosticAt(selectedRows.first().row())); +} + + +void SuppressedDiagnosticsModel::setDiagnostics(const SuppressedDiagnosticsList &diagnostics) +{ + beginResetModel(); + m_diagnostics = diagnostics; + endResetModel(); +} + +SuppressedDiagnostic SuppressedDiagnosticsModel::diagnosticAt(int i) const +{ + return m_diagnostics.at(i); +} + +int SuppressedDiagnosticsModel::rowCount(const QModelIndex &parent) const +{ + return parent.isValid() ? 0 : m_diagnostics.count(); +} + +QVariant SuppressedDiagnosticsModel::headerData(int section, Qt::Orientation orientation, + int role) const +{ + if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { + if (section == ColumnFile) + return tr("File"); + if (section == ColumnContext) + return tr("Context"); + if (section == ColumnDescription) + return tr("Diagnostic"); + } + return QVariant(); +} + +QVariant SuppressedDiagnosticsModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || role != Qt::DisplayRole || index.row() >= rowCount()) + return QVariant(); + const SuppressedDiagnostic &diag = m_diagnostics.at(index.row()); + if (index.column() == ColumnFile) + return diag.filePath.toUserOutput(); + if (index.column() == ColumnContext) { + if (diag.contextKind == QLatin1String("function") && !diag.context.isEmpty()) + return tr("Function \"%1\"").arg(diag.context); + return QString(); + } + if (index.column() == ColumnDescription) + return diag.description; + return QVariant(); +} + +} // namespace Internal +} // namespace ClangStaticAnalyzer + +#include "clangstaticanalyzerprojectsettingswidget.moc" diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingswidget.h b/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingswidget.h new file mode 100644 index 0000000000..6669919736 --- /dev/null +++ b/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingswidget.h @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us +** +** This file is part of the Qt Enterprise Qt Quick Profiler Add-on. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. +** +** If you have questions regarding the use of this file, please use +** contact form at http://www.qt.io/contact-us +** +****************************************************************************/ +#ifndef CLANGSTATICANALYZERPROJECTSETTINGSWIDGET_H +#define CLANGSTATICANALYZERPROJECTSETTINGSWIDGET_H + +#include + +namespace ProjectExplorer { class Project; } + +namespace ClangStaticAnalyzer { +namespace Internal { +class ProjectSettings; + +namespace Ui { class ProjectSettingsWidget; } + +class ProjectSettingsWidget : public QWidget +{ + Q_OBJECT + +public: + explicit ProjectSettingsWidget(ProjectExplorer::Project *project, QWidget *parent = 0); + ~ProjectSettingsWidget(); + +private: + void updateButtonStates(); + void updateButtonStateRemoveSelected(); + void updateButtonStateRemoveAll(); + void removeSelected(); + + Ui::ProjectSettingsWidget * const m_ui; + ProjectSettings * const m_projectSettings; +}; + +} // namespace Internal +} // namespace ClangStaticAnalyzer + +#endif // Include guard. diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingswidget.ui b/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingswidget.ui new file mode 100644 index 0000000000..c131bbe0c2 --- /dev/null +++ b/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingswidget.ui @@ -0,0 +1,87 @@ + + + ClangStaticAnalyzer::Internal::ProjectSettingsWidget + + + + 0 + 0 + 400 + 300 + + + + Form + + + + + + + + Suppressed Diagnostics: + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + QAbstractItemView::SingleSelection + + + + + + + + + Remove Selected + + + + + + + Remove All + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzertool.cpp b/plugins/clangstaticanalyzer/clangstaticanalyzertool.cpp index 260aae865a..645186cc71 100644 --- a/plugins/clangstaticanalyzer/clangstaticanalyzertool.cpp +++ b/plugins/clangstaticanalyzer/clangstaticanalyzertool.cpp @@ -51,6 +51,7 @@ namespace Internal { ClangStaticAnalyzerTool::ClangStaticAnalyzerTool(QObject *parent) : QObject(parent) , m_diagnosticModel(0) + , m_diagnosticFilterModel(0) , m_diagnosticView(0) , m_goBack(0) , m_goNext(0) @@ -74,10 +75,9 @@ QWidget *ClangStaticAnalyzerTool::createWidgets() m_diagnosticView->setFrameStyle(QFrame::NoFrame); m_diagnosticView->setAttribute(Qt::WA_MacShowFocusRect, false); m_diagnosticModel = new ClangStaticAnalyzerDiagnosticModel(m_diagnosticView); - // TODO: Make use of the proxy model - QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel(m_diagnosticView); - proxyModel->setSourceModel(m_diagnosticModel); - m_diagnosticView->setModel(proxyModel); + m_diagnosticFilterModel = new ClangStaticAnalyzerDiagnosticFilterModel(m_diagnosticView); + m_diagnosticFilterModel->setSourceModel(m_diagnosticModel); + m_diagnosticView->setModel(m_diagnosticFilterModel); m_diagnosticView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); m_diagnosticView->setAutoScroll(false); m_diagnosticView->setObjectName(QLatin1String("ClangStaticAnalyzerIssuesView")); @@ -203,6 +203,7 @@ void ClangStaticAnalyzerTool::startTool() setBusyCursor(true); Project *project = SessionManager::startupProject(); QTC_ASSERT(project, return); + m_diagnosticFilterModel->setProject(project); m_projectInfoBeforeBuild = CppTools::CppModelManager::instance()->projectInfo(project); QTC_ASSERT(m_projectInfoBeforeBuild.isValid(), return); m_running = true; @@ -241,15 +242,18 @@ void ClangStaticAnalyzerTool::onEngineFinished() QTC_ASSERT(m_goBack, return); QTC_ASSERT(m_goNext, return); QTC_ASSERT(m_diagnosticModel, return); + QTC_ASSERT(m_diagnosticFilterModel, return); resetCursorAndProjectInfoBeforeBuild(); const int issuesFound = m_diagnosticModel->rowCount(); - m_goBack->setEnabled(issuesFound > 1); - m_goNext->setEnabled(issuesFound > 1); + const int issuesVisible = m_diagnosticFilterModel->rowCount(); + m_goBack->setEnabled(issuesVisible > 1); + m_goNext->setEnabled(issuesVisible > 1); AnalyzerManager::showPermanentStatusMessage(issuesFound > 0 - ? AnalyzerManager::tr("Clang Static Analyzer finished, %n issues were found.", 0, issuesFound) + ? AnalyzerManager::tr("Clang Static Analyzer finished, %n issues were found (%1 suppressed).", + 0, issuesFound).arg(issuesFound - issuesVisible) : AnalyzerManager::tr("Clang Static Analyzer finished, no issues were found.")); m_running = false; emit finished(); diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzertool.h b/plugins/clangstaticanalyzer/clangstaticanalyzertool.h index fa402a608e..89c8851acc 100644 --- a/plugins/clangstaticanalyzer/clangstaticanalyzertool.h +++ b/plugins/clangstaticanalyzer/clangstaticanalyzertool.h @@ -27,6 +27,7 @@ namespace Analyzer { class DetailedErrorView; } namespace ClangStaticAnalyzer { namespace Internal { +class ClangStaticAnalyzerDiagnosticFilterModel; class ClangStaticAnalyzerDiagnosticModel; class ClangStaticAnalyzerDiagnosticView; class Diagnostic; @@ -65,6 +66,7 @@ private: CppTools::ProjectInfo m_projectInfoBeforeBuild; ClangStaticAnalyzerDiagnosticModel *m_diagnosticModel; + ClangStaticAnalyzerDiagnosticFilterModel *m_diagnosticFilterModel; Analyzer::DetailedErrorView *m_diagnosticView; QAction *m_goBack; -- cgit v1.2.1