summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Kandeler <christian.kandeler@theqtcompany.com>2015-02-19 18:08:38 +0100
committerChristian Kandeler <christian.kandeler@theqtcompany.com>2015-02-27 11:39:32 +0200
commit6e796591f445aa0f193b280b9c89fcf82fa2236d (patch)
treebcbd01ee08d56ca6964b90c303768b5a2b4ffa39
parenta13818f5cd3e8e60954dc75483834965926553fc (diff)
downloadqt-creator-6e796591f445aa0f193b280b9c89fcf82fa2236d.tar.gz
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 <nikolai.kosjar@theqtcompany.com>
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzer.pro9
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzer.qbs7
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticmodel.cpp71
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticmodel.h27
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticview.cpp47
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticview.h9
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzerplugin.cpp8
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettings.cpp137
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettings.h87
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingsmanager.cpp50
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingsmanager.h47
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingswidget.cpp160
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingswidget.h52
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingswidget.ui87
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzertool.cpp18
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzertool.h2
16 files changed, 808 insertions, 10 deletions
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 <projectexplorer/project.h>
+#include <projectexplorer/session.h>
+#include <utils/qtcassert.h>
+
#include <QCoreApplication>
+#include <QFileInfo>
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<ClangStaticAnalyzerDiagnosticModel *>(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 <utils/fileutils.h>
#include <QAbstractListModel>
+#include <QPointer>
+#include <QSortFilterProxyModel>
+
+namespace ProjectExplorer { class Project; }
namespace ClangStaticAnalyzer {
namespace Internal {
@@ -45,6 +52,26 @@ private:
QList<Diagnostic> 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<ProjectExplorer::Project> 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 <utils/fileutils.h>
#include <utils/qtcassert.h>
+#include <QAction>
#include <QCoreApplication>
#include <QDebug>
#include <QFileInfo>
@@ -196,13 +201,18 @@ DetailedErrorDelegate::SummaryLineInfo ClangStaticAnalyzerDiagnosticDelegate::su
return info;
}
+Diagnostic ClangStaticAnalyzerDiagnosticDelegate::getDiagnostic(const QModelIndex &index) const
+{
+ return index.data(Qt::UserRole).value<Diagnostic>();
+}
+
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<Diagnostic>();
+ 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<Diagnostic>();
+ 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<ClangStaticAnalyzerDiagnosticDelegate *>(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<ClangStaticAnalyzerDiagnosticFilterModel *>(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<QAction *> ClangStaticAnalyzerDiagnosticView::customActions() const
+{
+ return QList<QAction *>() << 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<QAction *> 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 <coreplugin/coreconstants.h>
#include <coreplugin/dialogs/ioptionspage.h>
#include <licensechecker/licensecheckerplugin.h>
+#include <projectexplorer/projectpanelfactory.h>
#include <extensionsystem/pluginmanager.h>
@@ -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<ProjectSettingsWidget>(QIcon());
+ ProjectExplorer::ProjectPanelFactory::registerFactory(panelFactory);
+
LicenseChecker::LicenseCheckerPlugin *licenseChecker
= ExtensionSystem::PluginManager::getObject<LicenseChecker::LicenseCheckerPlugin>();
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 <utils/qtcassert.h>
+
+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 <projectexplorer/project.h>
+#include <utils/fileutils.h>
+
+#include <QList>
+#include <QObject>
+
+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<SuppressedDiagnostic> 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 <projectexplorer/session.h>
+
+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 <QHash>
+#include <QSharedPointer>
+
+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<ProjectExplorer::Project *, QSharedPointer<ProjectSettings>> 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 <utils/qtcassert.h>
+
+#include <QAbstractTableModel>
+
+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<SuppressedDiagnosticsModel *>(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 <QWidget>
+
+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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ClangStaticAnalyzer::Internal::ProjectSettingsWidget</class>
+ <widget class="QWidget" name="ClangStaticAnalyzer::Internal::ProjectSettingsWidget">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>300</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Suppressed Diagnostics:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QTreeView" name="diagnosticsView">
+ <property name="selectionMode">
+ <enum>QAbstractItemView::SingleSelection</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QPushButton" name="removeSelectedButton">
+ <property name="text">
+ <string>Remove Selected</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="removeAllButton">
+ <property name="text">
+ <string>Remove All</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
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;