diff options
24 files changed, 967 insertions, 45 deletions
diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzer.pro b/plugins/clangstaticanalyzer/clangstaticanalyzer.pro index df939e9f36..67eb90ad41 100644 --- a/plugins/clangstaticanalyzer/clangstaticanalyzer.pro +++ b/plugins/clangstaticanalyzer/clangstaticanalyzer.pro @@ -10,7 +10,11 @@ SOURCES += \ clangstaticanalyzerdiagnosticmodel.cpp \ clangstaticanalyzerdiagnosticview.cpp \ clangstaticanalyzerlogfilereader.cpp \ + clangstaticanalyzerpathchooser.cpp \ clangstaticanalyzerplugin.cpp \ + clangstaticanalyzerprojectsettings.cpp \ + clangstaticanalyzerprojectsettingsmanager.cpp \ + clangstaticanalyzerprojectsettingswidget.cpp \ clangstaticanalyzerruncontrol.cpp \ clangstaticanalyzerruncontrolfactory.cpp \ clangstaticanalyzerrunner.cpp \ @@ -26,7 +30,11 @@ HEADERS += \ clangstaticanalyzerdiagnosticview.h \ clangstaticanalyzer_global.h \ clangstaticanalyzerlogfilereader.h \ + clangstaticanalyzerpathchooser.h \ clangstaticanalyzerplugin.h \ + clangstaticanalyzerprojectsettings.h \ + clangstaticanalyzerprojectsettingsmanager.h \ + clangstaticanalyzerprojectsettingswidget.h \ clangstaticanalyzerruncontrolfactory.h \ clangstaticanalyzerruncontrol.h \ clangstaticanalyzerrunner.h \ @@ -35,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 78c7decc5d..a97dc9f83c 100644 --- a/plugins/clangstaticanalyzer/clangstaticanalyzer.qbs +++ b/plugins/clangstaticanalyzer/clangstaticanalyzer.qbs @@ -28,8 +28,17 @@ QtcPlugin { "clangstaticanalyzerdiagnosticview.h", "clangstaticanalyzerlogfilereader.cpp", "clangstaticanalyzerlogfilereader.h", + "clangstaticanalyzerpathchooser.cpp", + "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/clangstaticanalyzerconfigwidget.cpp b/plugins/clangstaticanalyzer/clangstaticanalyzerconfigwidget.cpp index d6ff0fa5b8..6b8b0f0cee 100644 --- a/plugins/clangstaticanalyzer/clangstaticanalyzerconfigwidget.cpp +++ b/plugins/clangstaticanalyzer/clangstaticanalyzerconfigwidget.cpp @@ -33,10 +33,6 @@ ClangStaticAnalyzerConfigWidget::ClangStaticAnalyzerConfigWidget( { m_ui->setupUi(this); - m_ui->clangExecutableChooser->setExpectedKind(Utils::PathChooser::ExistingCommand); - m_ui->clangExecutableChooser->setHistoryCompleter( - QLatin1String("ClangStaticAnalyzer.ClangCommand.History")); - m_ui->clangExecutableChooser->setPromptDialogTitle(tr("Clang Command")); m_ui->clangExecutableChooser->setPath(settings->clangExecutable()); connect(m_ui->clangExecutableChooser, &Utils::PathChooser::changed, m_settings, &ClangStaticAnalyzerSettings::setClangExecutable); diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerconfigwidget.ui b/plugins/clangstaticanalyzer/clangstaticanalyzerconfigwidget.ui index 800733a331..8997f3a7d1 100644 --- a/plugins/clangstaticanalyzer/clangstaticanalyzerconfigwidget.ui +++ b/plugins/clangstaticanalyzer/clangstaticanalyzerconfigwidget.ui @@ -30,7 +30,7 @@ <item row="0" column="1"> <layout class="QHBoxLayout" name="horizontalLayout"> <item> - <widget class="Utils::PathChooser" name="clangExecutableChooser" native="true"/> + <widget class="ClangStaticAnalyzer::Internal::PathChooser" name="clangExecutableChooser" native="true"/> </item> </layout> </item> @@ -88,9 +88,9 @@ </widget> <customwidgets> <customwidget> - <class>Utils::PathChooser</class> + <class>ClangStaticAnalyzer::Internal::PathChooser</class> <extends>QWidget</extends> - <header location="global">utils/pathchooser.h</header> + <header location="global">clangstaticanalyzer/clangstaticanalyzerpathchooser.h</header> <container>1</container> </customwidget> </customwidgets> 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/clangstaticanalyzerpathchooser.cpp b/plugins/clangstaticanalyzer/clangstaticanalyzerpathchooser.cpp new file mode 100644 index 0000000000..b5711bfeab --- /dev/null +++ b/plugins/clangstaticanalyzer/clangstaticanalyzerpathchooser.cpp @@ -0,0 +1,40 @@ +/**************************************************************************** +** +** 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 LicenseChecker 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 "clangstaticanalyzerpathchooser.h" + +#include "clangstaticanalyzerutils.h" + +namespace ClangStaticAnalyzer { +namespace Internal { + +PathChooser::PathChooser(QWidget *parent) : Utils::PathChooser(parent) +{ + setExpectedKind(Utils::PathChooser::ExistingCommand); + setHistoryCompleter(QLatin1String("ClangStaticAnalyzer.ClangCommand.History")); + setPromptDialogTitle(tr("Clang Command")); +} + +bool PathChooser::validatePath(const QString &path, QString *errorMessage) +{ + if (!Utils::PathChooser::validatePath(path, errorMessage)) + return false; + return isClangExecutableUsable(fileName().toString(), errorMessage); +} + +} // namespace Internal +} // namespace ClangStaticAnalyzer diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerpathchooser.h b/plugins/clangstaticanalyzer/clangstaticanalyzerpathchooser.h new file mode 100644 index 0000000000..b4bc3c5947 --- /dev/null +++ b/plugins/clangstaticanalyzer/clangstaticanalyzerpathchooser.h @@ -0,0 +1,40 @@ +/**************************************************************************** +** +** 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 LicenseChecker 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 QTC_CLANGSTATICANALYZER_PATHCHOOSER_H +#define QTC_CLANGSTATICANALYZER_PATHCHOOSER_H + +#include <utils/pathchooser.h> + +namespace ClangStaticAnalyzer { +namespace Internal { + +class PathChooser : public Utils::PathChooser +{ + Q_OBJECT + +public: + PathChooser(QWidget *parent = 0); + +private: + bool validatePath(const QString &path, QString *errorMessage = 0); +}; + +} // namespace Internal +} // namespace ClangStaticAnalyzer + +#endif // Include guard. diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerplugin.cpp b/plugins/clangstaticanalyzer/clangstaticanalyzerplugin.cpp index 1c09b0039e..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>(); @@ -125,16 +133,26 @@ bool ClangStaticAnalyzerPlugin::initializeEnterpriseFeatures(const QStringList & Q_UNUSED(arguments); Q_UNUSED(errorString); - m_analyzerTool = new ClangStaticAnalyzerTool(this); + auto tool = m_analyzerTool = new ClangStaticAnalyzerTool(this); addAutoReleasedObject(new ClangStaticAnalyzerRunControlFactory(m_analyzerTool)); addAutoReleasedObject(new ClangStaticAnalyzerOptionsPage); + auto widgetCreator = [tool] { return tool->createWidgets(); }; + auto runControlCreator = [tool](const AnalyzerStartParameters &sp, + ProjectExplorer::RunConfiguration *runConfiguration) { + return tool->createRunControl(sp, runConfiguration); + }; + const QString toolTip = tr("Clang Static Analyzer uses the analyzer from the clang project " "to find bugs."); AnalyzerAction *action = new AnalyzerAction(this); - action->setId("ClangStaticAnalyzer"); - action->setTool(m_analyzerTool); + action->setRunMode(ProjectExplorer::ClangStaticAnalyzerMode); + action->setToolId(ClangStaticAnalyzerToolId); + action->setActionId("ClangStaticAnalyzer"); + action->setWidgetCreator(widgetCreator); + action->setRunControlCreator(runControlCreator); + action->setToolStarter([tool] { tool->startTool(); }); action->setText(tr("Clang Static Analyzer")); action->setToolTip(toolTip); action->setMenuGroup(Constants::G_ANALYZER_TOOLS); 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/clangstaticanalyzerruncontrol.cpp b/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.cpp index 15deb7b9ce..e0576299e4 100644 --- a/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.cpp +++ b/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.cpp @@ -23,6 +23,7 @@ #include "clangstaticanalyzersettings.h" #include "clangstaticanalyzerutils.h" +#include <analyzerbase/analyzerconstants.h> #include <analyzerbase/analyzermanager.h> #include <clangcodemodel/clangutils.h> @@ -37,6 +38,7 @@ #include <projectexplorer/kitinformation.h> #include <projectexplorer/project.h> #include <projectexplorer/target.h> +#include <projectexplorer/taskhub.h> #include <utils/algorithm.h> @@ -51,6 +53,14 @@ static Q_LOGGING_CATEGORY(LOG, "qtc.clangstaticanalyzer.runcontrol") namespace ClangStaticAnalyzer { namespace Internal { +static void logToIssuesPane(Task::TaskType type, const QString &message) +{ + TaskHub::addTask(type, message, Analyzer::Constants::ANALYZERTASK_ID); + if (type == Task::Error) + TaskHub::requestPopup(); +} + + ClangStaticAnalyzerRunControl::ClangStaticAnalyzerRunControl( const Analyzer::AnalyzerStartParameters &startParams, ProjectExplorer::RunConfiguration *runConfiguration, @@ -191,9 +201,10 @@ bool ClangStaticAnalyzerRunControl::startEngine() const QString executable = clangExecutableFromSettings(m_toolchainType, &isValidClangExecutable); if (!isValidClangExecutable) { - emit appendMessage(tr("Clang Static Analyzer: Invalid executable \"%1\", stop.") - .arg(executable) + QLatin1Char('\n'), - Utils::ErrorMessageFormat); + const QString errorMessage = tr("Clang Static Analyzer: Invalid executable \"%1\", stop.") + .arg(executable); + appendMessage(errorMessage + QLatin1Char('\n'), Utils::ErrorMessageFormat); + logToIssuesPane(Task::Error, errorMessage); emit finished(); return false; } @@ -203,8 +214,10 @@ bool ClangStaticAnalyzerRunControl::startEngine() QTemporaryDir temporaryDir(QDir::tempPath() + QLatin1String("/qtc-clangstaticanalyzer-XXXXXX")); temporaryDir.setAutoRemove(false); if (!temporaryDir.isValid()) { - emit appendMessage(tr("Clang Static Analyzer: Failed to create temporary dir, stop.") - + QLatin1Char('\n'), Utils::ErrorMessageFormat); + const QString errorMessage + = tr("Clang Static Analyzer: Failed to create temporary dir, stop."); + appendMessage(errorMessage + QLatin1Char('\n'), Utils::ErrorMessageFormat); + logToIssuesPane(Task::Error, errorMessage); emit finished(); return false; } @@ -273,6 +286,10 @@ void ClangStaticAnalyzerRunControl::analyzeNextFile() .arg(m_filesNotAnalyzed) + QLatin1Char('\n'), Utils::NormalMessageFormat); + if (m_filesAnalyzed == 0 && m_filesNotAnalyzed != 0) { + logToIssuesPane(Task::Error, + tr("Clang Static Analyzer: Failed to analyze any files.")); + } m_progress.reportFinished(); emit finished(); } @@ -336,7 +353,8 @@ void ClangStaticAnalyzerRunControl::onRunnerFinishedWithFailure(const QString &e + QLatin1Char('\n') , Utils::StdErrFormat); appendMessage(errorDetails, Utils::StdErrFormat); - + logToIssuesPane(Task::Warning, errorMessage); + logToIssuesPane(Task::Warning, errorDetails); handleFinished(); } diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzertool.cpp b/plugins/clangstaticanalyzer/clangstaticanalyzertool.cpp index 4fbc2aa5b5..645186cc71 100644 --- a/plugins/clangstaticanalyzer/clangstaticanalyzertool.cpp +++ b/plugins/clangstaticanalyzer/clangstaticanalyzertool.cpp @@ -49,16 +49,15 @@ namespace ClangStaticAnalyzer { namespace Internal { ClangStaticAnalyzerTool::ClangStaticAnalyzerTool(QObject *parent) - : IAnalyzerTool(parent) + : QObject(parent) , m_diagnosticModel(0) + , m_diagnosticFilterModel(0) , m_diagnosticView(0) , m_goBack(0) , m_goNext(0) , m_running(false) { setObjectName(QLatin1String("ClangStaticAnalyzerTool")); - setRunMode(ProjectExplorer::ClangStaticAnalyzerMode); - setToolMode(AnyMode); } QWidget *ClangStaticAnalyzerTool::createWidgets() @@ -76,16 +75,16 @@ 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")); m_diagnosticView->setWindowTitle(tr("Clang Static Analyzer Issues")); - QDockWidget *issuesDock = AnalyzerManager::createDockWidget(this, m_diagnosticView); + QDockWidget *issuesDock = AnalyzerManager::createDockWidget(ClangStaticAnalyzerToolId, + m_diagnosticView); issuesDock->show(); Utils::FancyMainWindow *mw = AnalyzerManager::mainWindow(); mw->splitDockWidget(mw->toolBarDockWidget(), issuesDock, Qt::Vertical); @@ -192,23 +191,23 @@ static bool dontStartAfterHintForDebugMode() return false; } -void ClangStaticAnalyzerTool::startTool(StartMode mode) +void ClangStaticAnalyzerTool::startTool() { - QTC_ASSERT(mode == Analyzer::StartLocal, return); - AnalyzerManager::showMode(); if (dontStartAfterHintForDebugMode()) return; + AnalyzerManager::showPermanentStatusMessage(QString()); m_diagnosticModel->clear(); 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; - ProjectExplorerPlugin::instance()->runProject(project, runMode()); + ProjectExplorerPlugin::runProject(project, ProjectExplorer::ClangStaticAnalyzerMode); } CppTools::ProjectInfo ClangStaticAnalyzerTool::projectInfoBeforeBuild() const @@ -243,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::showStatusMessage(issuesFound > 0 - ? AnalyzerManager::tr("Clang Static Analyzer finished, %n issues were found.", 0, issuesFound) + AnalyzerManager::showPermanentStatusMessage(issuesFound > 0 + ? 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 24c39e3b39..89c8851acc 100644 --- a/plugins/clangstaticanalyzer/clangstaticanalyzertool.h +++ b/plugins/clangstaticanalyzer/clangstaticanalyzertool.h @@ -27,11 +27,14 @@ namespace Analyzer { class DetailedErrorView; } namespace ClangStaticAnalyzer { namespace Internal { +class ClangStaticAnalyzerDiagnosticFilterModel; class ClangStaticAnalyzerDiagnosticModel; class ClangStaticAnalyzerDiagnosticView; class Diagnostic; -class ClangStaticAnalyzerTool : public Analyzer::IAnalyzerTool +const char ClangStaticAnalyzerToolId[] = "ClangStaticAnalyzer"; + +class ClangStaticAnalyzerTool : public QObject { Q_OBJECT @@ -44,15 +47,15 @@ public: bool isRunning() const { return m_running; } QList<Diagnostic> diagnostics() const; -signals: - void finished(); // For testing. - -private: QWidget *createWidgets(); Analyzer::AnalyzerRunControl *createRunControl(const Analyzer::AnalyzerStartParameters &sp, ProjectExplorer::RunConfiguration *runConfiguration); - void startTool(Analyzer::StartMode mode); + void startTool(); +signals: + void finished(); // For testing. + +private: void onEngineIsStarting(); void onNewDiagnosticsAvailable(const QList<Diagnostic> &diagnostics); void onEngineFinished(); @@ -63,6 +66,7 @@ private: CppTools::ProjectInfo m_projectInfoBeforeBuild; ClangStaticAnalyzerDiagnosticModel *m_diagnosticModel; + ClangStaticAnalyzerDiagnosticFilterModel *m_diagnosticFilterModel; Analyzer::DetailedErrorView *m_diagnosticView; QAction *m_goBack; diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerunittests.cpp b/plugins/clangstaticanalyzer/clangstaticanalyzerunittests.cpp index 401359cc6c..21b086858e 100644 --- a/plugins/clangstaticanalyzer/clangstaticanalyzerunittests.cpp +++ b/plugins/clangstaticanalyzer/clangstaticanalyzerunittests.cpp @@ -81,7 +81,7 @@ void ClangStaticAnalyzerUnitTests::testProject() CppTools::Tests::ProjectOpenerAndCloser projectManager; const CppTools::ProjectInfo projectInfo = projectManager.open(projectFilePath, true); QVERIFY(projectInfo.isValid()); - AnalyzerManager::selectTool(m_analyzerTool, Analyzer::StartLocal); + AnalyzerManager::selectTool(ClangStaticAnalyzerToolId); AnalyzerManager::startTool(); if (m_analyzerTool->isRunning()) { QSignalSpy waiter(m_analyzerTool, SIGNAL(finished())); diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerutils.cpp b/plugins/clangstaticanalyzer/clangstaticanalyzerutils.cpp index 06401eb287..3860f929a6 100644 --- a/plugins/clangstaticanalyzer/clangstaticanalyzerutils.cpp +++ b/plugins/clangstaticanalyzer/clangstaticanalyzerutils.cpp @@ -23,6 +23,7 @@ #include <utils/environment.h> +#include <QCoreApplication> #include <QFileInfo> static bool isFileExecutable(const QString &executablePath) @@ -63,7 +64,7 @@ QString clangExecutable(const QString &fileNameOrPath, bool *isValid) executable = executableFromPath; } - *isValid = isFileExecutable(executable); + *isValid = isFileExecutable(executable) && isClangExecutableUsable(executable); return executable; } @@ -74,5 +75,20 @@ QString createFullLocationString(const ClangStaticAnalyzer::Internal::Location & return filePath + QLatin1Char(':') + lineNumber; } +bool isClangExecutableUsable(const QString &filePath, QString *errorMessage) +{ + const QFileInfo fi(filePath); + if (fi.isSymLink() && fi.symLinkTarget().contains(QLatin1String("icecc"))) { + if (errorMessage) { + *errorMessage = QCoreApplication::translate("ClangStaticAnalyzer", + "The chosen file \"%1\" seems to point to an icecc binary not suitable " + "for analyzing.\nPlease set a real clang executable.") + .arg(filePath); + } + return false; + } + return true; +} + } // namespace Internal } // namespace ClangStaticAnalyzer diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerutils.h b/plugins/clangstaticanalyzer/clangstaticanalyzerutils.h index e85d25f286..ca06c4874e 100644 --- a/plugins/clangstaticanalyzer/clangstaticanalyzerutils.h +++ b/plugins/clangstaticanalyzer/clangstaticanalyzerutils.h @@ -25,13 +25,13 @@ QT_BEGIN_NAMESPACE class QString; QT_END_NAMESPACE - - namespace ClangStaticAnalyzer { namespace Internal { class Location; +bool isClangExecutableUsable(const QString &filePath, QString *errorMessage = 0); + QString clangExecutable(const QString &fileNameOrPath, bool *isValid); QString clangExecutableFromSettings(const QString &toolchainType, bool *isValid); |