summaryrefslogtreecommitdiff
path: root/src/plugins/clangstaticanalyzer
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/clangstaticanalyzer')
-rw-r--r--src/plugins/clangstaticanalyzer/ClangStaticAnalyzer.json.in19
-rw-r--r--src/plugins/clangstaticanalyzer/clangstaticanalyzer.pro54
-rw-r--r--src/plugins/clangstaticanalyzer/clangstaticanalyzer.qbs74
-rw-r--r--src/plugins/clangstaticanalyzer/clangstaticanalyzer_dependencies.pri10
-rw-r--r--src/plugins/clangstaticanalyzer/clangstaticanalyzer_global.h38
-rw-r--r--src/plugins/clangstaticanalyzer/clangstaticanalyzerconfigwidget.cpp92
-rw-r--r--src/plugins/clangstaticanalyzer/clangstaticanalyzerconfigwidget.h55
-rw-r--r--src/plugins/clangstaticanalyzer/clangstaticanalyzerconfigwidget.ui99
-rw-r--r--src/plugins/clangstaticanalyzer/clangstaticanalyzerconstants.h38
-rw-r--r--src/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnostic.cpp47
-rw-r--r--src/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnostic.h71
-rw-r--r--src/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticmodel.cpp357
-rw-r--r--src/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticmodel.h82
-rw-r--r--src/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticview.cpp84
-rw-r--r--src/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticview.h52
-rw-r--r--src/plugins/clangstaticanalyzer/clangstaticanalyzerlogfilereader.cpp381
-rw-r--r--src/plugins/clangstaticanalyzer/clangstaticanalyzerlogfilereader.h45
-rw-r--r--src/plugins/clangstaticanalyzer/clangstaticanalyzerplugin.cpp184
-rw-r--r--src/plugins/clangstaticanalyzer/clangstaticanalyzerplugin.h61
-rw-r--r--src/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettings.cpp145
-rw-r--r--src/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettings.h95
-rw-r--r--src/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingsmanager.cpp58
-rw-r--r--src/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingsmanager.h55
-rw-r--r--src/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingswidget.cpp168
-rw-r--r--src/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingswidget.h60
-rw-r--r--src/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingswidget.ui87
-rw-r--r--src/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.cpp511
-rw-r--r--src/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.h101
-rw-r--r--src/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrolfactory.cpp122
-rw-r--r--src/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrolfactory.h58
-rw-r--r--src/plugins/clangstaticanalyzer/clangstaticanalyzerrunner.cpp193
-rw-r--r--src/plugins/clangstaticanalyzer/clangstaticanalyzerrunner.h86
-rw-r--r--src/plugins/clangstaticanalyzer/clangstaticanalyzersettings.cpp122
-rw-r--r--src/plugins/clangstaticanalyzer/clangstaticanalyzersettings.h59
-rw-r--r--src/plugins/clangstaticanalyzer/clangstaticanalyzertool.cpp332
-rw-r--r--src/plugins/clangstaticanalyzer/clangstaticanalyzertool.h93
-rw-r--r--src/plugins/clangstaticanalyzer/clangstaticanalyzerunittests.cpp117
-rw-r--r--src/plugins/clangstaticanalyzer/clangstaticanalyzerunittests.h60
-rw-r--r--src/plugins/clangstaticanalyzer/clangstaticanalyzerunittests.qrc13
-rw-r--r--src/plugins/clangstaticanalyzer/clangstaticanalyzerutils.cpp103
-rw-r--r--src/plugins/clangstaticanalyzer/clangstaticanalyzerutils.h52
-rw-r--r--src/plugins/clangstaticanalyzer/tests/clangstaticanalyzerautotest.qbs11
-rw-r--r--src/plugins/clangstaticanalyzer/tests/clangstaticanalyzerlogfilereader/clangstaticanalyzerlogfilereader.pro14
-rw-r--r--src/plugins/clangstaticanalyzer/tests/clangstaticanalyzerlogfilereader/clangstaticanalyzerlogfilereader.qbs21
-rw-r--r--src/plugins/clangstaticanalyzer/tests/clangstaticanalyzerlogfilereader/data/noDiagnostics.plist14
-rw-r--r--src/plugins/clangstaticanalyzer/tests/clangstaticanalyzerlogfilereader/data/someDiagnostics.plist162
-rw-r--r--src/plugins/clangstaticanalyzer/tests/clangstaticanalyzerlogfilereader/tst_clangstaticanalyzerlogfilereader.cpp162
-rw-r--r--src/plugins/clangstaticanalyzer/tests/clangstaticanalyzerrunner/clangstaticanalyzerrunner.pro11
-rw-r--r--src/plugins/clangstaticanalyzer/tests/clangstaticanalyzerrunner/clangstaticanalyzerrunner.qbs19
-rw-r--r--src/plugins/clangstaticanalyzer/tests/clangstaticanalyzerrunner/tst_clangstaticanalyzerrunner.cpp176
-rw-r--r--src/plugins/clangstaticanalyzer/tests/tests.pri22
-rw-r--r--src/plugins/clangstaticanalyzer/tests/tests.pro6
-rw-r--r--src/plugins/clangstaticanalyzer/tests/tests.qbs8
-rw-r--r--src/plugins/clangstaticanalyzer/unit-tests/qt-widgets-app/main.cpp11
-rw-r--r--src/plugins/clangstaticanalyzer/unit-tests/qt-widgets-app/mainwindow.cpp14
-rw-r--r--src/plugins/clangstaticanalyzer/unit-tests/qt-widgets-app/mainwindow.h24
-rw-r--r--src/plugins/clangstaticanalyzer/unit-tests/qt-widgets-app/mainwindow.ui24
-rw-r--r--src/plugins/clangstaticanalyzer/unit-tests/qt-widgets-app/qt-widgets-app.pro8
-rw-r--r--src/plugins/clangstaticanalyzer/unit-tests/qt-widgets-app/qt-widgets-app.qbs17
-rw-r--r--src/plugins/clangstaticanalyzer/unit-tests/simple/main.cpp5
-rw-r--r--src/plugins/clangstaticanalyzer/unit-tests/simple/simple.pro3
-rw-r--r--src/plugins/clangstaticanalyzer/unit-tests/simple/simple.qbs5
62 files changed, 5270 insertions, 0 deletions
diff --git a/src/plugins/clangstaticanalyzer/ClangStaticAnalyzer.json.in b/src/plugins/clangstaticanalyzer/ClangStaticAnalyzer.json.in
new file mode 100644
index 0000000000..8be50b27da
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/ClangStaticAnalyzer.json.in
@@ -0,0 +1,19 @@
+{
+ \"Name\" : \"ClangStaticAnalyzer\",
+ \"Version\" : \"$$QTCREATOR_VERSION\",
+ \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\",
+ \"Vendor\" : \"The Qt Company Ltd\",
+ \"Copyright\" : \"(C) 2016 The Qt Company Ltd\",
+ \"License\" : [ \"Commercial Usage\",
+ \"\",
+ \"Licensees holding valid commercial Qt licenses may use this file in accordance with the commercial license agreement provided with the Software or, alternatively, in accordance with the terms contained in a written agreement between you and The Qt Company. For licensing terms and conditions see https://www.qt.io/terms-conditions. For further information use the contact form at https://www.qt.io/contact-us.\",
+ \"\",
+ \"GNU General Public License Usage\",
+ \"\",
+ \"Alternatively, this file may be used under the terms of the GNU General Public License version 3 as published by the Free Software Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT included in the packaging of this file. Please review the following information to ensure the GNU General Public License requirements will be met: https://www.gnu.org/licenses/gpl-3.0.html.\"
+ ],
+ \"Category\" : \"Code Analyzer\",
+ \"Description\" : \"ClangStaticAnalyzer Plugin.\",
+ \"Url\" : \"http://www.qt.io\",
+ $$dependencyList
+}
diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzer.pro b/src/plugins/clangstaticanalyzer/clangstaticanalyzer.pro
new file mode 100644
index 0000000000..cdffb1f1d1
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzer.pro
@@ -0,0 +1,54 @@
+TARGET = ClangStaticAnalyzer
+TEMPLATE = lib
+
+include(../../qtcreatorplugin.pri)
+include(clangstaticanalyzer_dependencies.pri)
+
+SOURCES += \
+ clangstaticanalyzerconfigwidget.cpp \
+ clangstaticanalyzerdiagnostic.cpp \
+ clangstaticanalyzerdiagnosticmodel.cpp \
+ clangstaticanalyzerdiagnosticview.cpp \
+ clangstaticanalyzerlogfilereader.cpp \
+ clangstaticanalyzerplugin.cpp \
+ clangstaticanalyzerprojectsettings.cpp \
+ clangstaticanalyzerprojectsettingsmanager.cpp \
+ clangstaticanalyzerprojectsettingswidget.cpp \
+ clangstaticanalyzerruncontrol.cpp \
+ clangstaticanalyzerruncontrolfactory.cpp \
+ clangstaticanalyzerrunner.cpp \
+ clangstaticanalyzersettings.cpp \
+ clangstaticanalyzertool.cpp \
+ clangstaticanalyzerutils.cpp
+
+HEADERS += \
+ clangstaticanalyzerconfigwidget.h \
+ clangstaticanalyzerconstants.h \
+ clangstaticanalyzerdiagnostic.h \
+ clangstaticanalyzerdiagnosticmodel.h \
+ clangstaticanalyzerdiagnosticview.h \
+ clangstaticanalyzer_global.h \
+ clangstaticanalyzerlogfilereader.h \
+ clangstaticanalyzerplugin.h \
+ clangstaticanalyzerprojectsettings.h \
+ clangstaticanalyzerprojectsettingsmanager.h \
+ clangstaticanalyzerprojectsettingswidget.h \
+ clangstaticanalyzerruncontrolfactory.h \
+ clangstaticanalyzerruncontrol.h \
+ clangstaticanalyzerrunner.h \
+ clangstaticanalyzersettings.h \
+ clangstaticanalyzertool.h \
+ clangstaticanalyzerutils.h
+
+FORMS += \
+ clangstaticanalyzerconfigwidget.ui \
+ clangstaticanalyzerprojectsettingswidget.ui
+
+equals(TEST, 1) {
+ HEADERS += clangstaticanalyzerunittests.h
+ SOURCES += clangstaticanalyzerunittests.cpp
+ RESOURCES += clangstaticanalyzerunittests.qrc
+}
+
+DISTFILES += \
+ tests/tests.pri
diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzer.qbs b/src/plugins/clangstaticanalyzer/clangstaticanalyzer.qbs
new file mode 100644
index 0000000000..2562b46b3c
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzer.qbs
@@ -0,0 +1,74 @@
+import qbs
+
+QtcPlugin {
+ name: "ClangStaticAnalyzer"
+
+ Depends { name: "AnalyzerBase" }
+ Depends { name: "Core" }
+ Depends { name: "CppTools" }
+ Depends { name: "ExtensionSystem" }
+ Depends { name: "ProjectExplorer" }
+ Depends { name: "QtcSsh" }
+ Depends { name: "Utils" }
+
+ Depends { name: "Qt.widgets" }
+
+ pluginTestDepends: [
+ "QbsProjectManager",
+ "QmakeProjectManager",
+ ]
+
+ files: [
+ "clangstaticanalyzerconfigwidget.cpp",
+ "clangstaticanalyzerconfigwidget.h",
+ "clangstaticanalyzerconfigwidget.ui",
+ "clangstaticanalyzerconstants.h",
+ "clangstaticanalyzerdiagnostic.cpp",
+ "clangstaticanalyzerdiagnostic.h",
+ "clangstaticanalyzerdiagnosticmodel.cpp",
+ "clangstaticanalyzerdiagnosticmodel.h",
+ "clangstaticanalyzerdiagnosticview.cpp",
+ "clangstaticanalyzerdiagnosticview.h",
+ "clangstaticanalyzerlogfilereader.cpp",
+ "clangstaticanalyzerlogfilereader.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",
+ "clangstaticanalyzerruncontrolfactory.h",
+ "clangstaticanalyzerrunner.cpp",
+ "clangstaticanalyzerrunner.h",
+ "clangstaticanalyzersettings.cpp",
+ "clangstaticanalyzersettings.h",
+ "clangstaticanalyzertool.cpp",
+ "clangstaticanalyzertool.h",
+ "clangstaticanalyzerutils.cpp",
+ "clangstaticanalyzerutils.h",
+ "clangstaticanalyzer_global.h",
+ ]
+
+ Group {
+ name: "Unit tests"
+ condition: project.testsEnabled
+ files: [
+ "clangstaticanalyzerunittests.cpp",
+ "clangstaticanalyzerunittests.h",
+ "clangstaticanalyzerunittests.qrc",
+ ]
+ }
+
+ Group {
+ name: "Unit test resources"
+ prefix: "unit-tests/"
+ fileTags: []
+ files: ["**/*"]
+ }
+}
diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzer_dependencies.pri b/src/plugins/clangstaticanalyzer/clangstaticanalyzer_dependencies.pri
new file mode 100644
index 0000000000..b099392373
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzer_dependencies.pri
@@ -0,0 +1,10 @@
+QTC_PLUGIN_NAME = ClangStaticAnalyzer
+QTC_LIB_DEPENDS += \
+ extensionsystem \
+ utils
+QTC_PLUGIN_DEPENDS += \
+ analyzerbase \
+ cpptools
+QTC_TEST_DEPENDS += \
+ qbsprojectmanager \
+ qmakeprojectmanager
diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzer_global.h b/src/plugins/clangstaticanalyzer/clangstaticanalyzer_global.h
new file mode 100644
index 0000000000..39fb523976
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzer_global.h
@@ -0,0 +1,38 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#ifndef CLANGSTATICANALYZER_GLOBAL_H
+#define CLANGSTATICANALYZER_GLOBAL_H
+
+#include <QtGlobal>
+
+#if defined(CLANGSTATICANALYZER_LIBRARY)
+# define CLANGSTATICANALYZER_EXPORT Q_DECL_EXPORT
+#else
+# define CLANGSTATICANALYZER_EXPORT Q_DECL_IMPORT
+#endif
+
+#endif // CLANGSTATICANALYZER_GLOBAL_H
+
diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzerconfigwidget.cpp b/src/plugins/clangstaticanalyzer/clangstaticanalyzerconfigwidget.cpp
new file mode 100644
index 0000000000..0ffe9ca8c2
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerconfigwidget.cpp
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "clangstaticanalyzerconfigwidget.h"
+#include "ui_clangstaticanalyzerconfigwidget.h"
+
+#include "clangstaticanalyzerutils.h"
+
+#include <QThread>
+
+namespace ClangStaticAnalyzer {
+namespace Internal {
+
+ClangStaticAnalyzerConfigWidget::ClangStaticAnalyzerConfigWidget(
+ ClangStaticAnalyzerSettings *settings,
+ QWidget *parent)
+ : QWidget(parent)
+ , m_ui(new Ui::ClangStaticAnalyzerConfigWidget)
+ , m_settings(settings)
+{
+ m_ui->setupUi(this);
+
+ Utils::PathChooser * const chooser = m_ui->clangExecutableChooser;
+ chooser->setExpectedKind(Utils::PathChooser::ExistingCommand);
+ chooser->setHistoryCompleter(QLatin1String("ClangStaticAnalyzer.ClangCommand.History"));
+ chooser->setPromptDialogTitle(tr("Clang Command"));
+ const auto validator = [chooser](Utils::FancyLineEdit *edit, QString *errorMessage) {
+ const QString currentFilePath = chooser->fileName().toString();
+ Utils::PathChooser pc;
+ Utils::PathChooser *helperPathChooser;
+ if (currentFilePath.isEmpty()) {
+ pc.setExpectedKind(chooser->expectedKind());
+ pc.setPath(edit->placeholderText());
+ helperPathChooser = &pc;
+ } else {
+ helperPathChooser = chooser;
+ }
+ return chooser->defaultValidationFunction()(helperPathChooser->lineEdit(), errorMessage)
+ && isClangExecutableUsable(helperPathChooser->fileName().toString(), errorMessage);
+ };
+ chooser->setValidationFunction(validator);
+ bool clangExeIsSet;
+ const QString clangExe = settings->clangExecutable(&clangExeIsSet);
+ chooser->lineEdit()->setPlaceholderText(settings->defaultClangExecutable());
+ if (clangExeIsSet) {
+ chooser->setPath(clangExe);
+ } else {
+ // Setting an empty string does not trigger the validator, as that is the initial value
+ // in the line edit.
+ chooser->setPath(QLatin1String(" "));
+ chooser->lineEdit()->clear();
+ }
+ connect(m_ui->clangExecutableChooser, &Utils::PathChooser::rawPathChanged,
+ [settings](const QString &path) { settings->setClangExecutable(path); });
+
+ m_ui->simultaneousProccessesSpinBox->setValue(settings->simultaneousProcesses());
+ m_ui->simultaneousProccessesSpinBox->setMinimum(1);
+ m_ui->simultaneousProccessesSpinBox->setMaximum(QThread::idealThreadCount());
+ connect(m_ui->simultaneousProccessesSpinBox,
+ static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
+ [settings](int count) { settings->setSimultaneousProcesses(count); });
+}
+
+ClangStaticAnalyzerConfigWidget::~ClangStaticAnalyzerConfigWidget()
+{
+ delete m_ui;
+}
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzer
diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzerconfigwidget.h b/src/plugins/clangstaticanalyzer/clangstaticanalyzerconfigwidget.h
new file mode 100644
index 0000000000..4655a07eec
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerconfigwidget.h
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#ifndef CLANGSTATICANALYZERCONFIGWIDGET_H
+#define CLANGSTATICANALYZERCONFIGWIDGET_H
+
+#include "clangstaticanalyzersettings.h"
+
+#include <QWidget>
+
+namespace ClangStaticAnalyzer {
+namespace Internal {
+
+namespace Ui { class ClangStaticAnalyzerConfigWidget; }
+
+class ClangStaticAnalyzerConfigWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit ClangStaticAnalyzerConfigWidget(ClangStaticAnalyzerSettings *settings,
+ QWidget *parent = 0);
+ ~ClangStaticAnalyzerConfigWidget();
+
+private:
+ Ui::ClangStaticAnalyzerConfigWidget *m_ui;
+ ClangStaticAnalyzerSettings *m_settings;
+};
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzer
+
+#endif // CLANGSTATICANALYZERCONFIGWIDGET_H
diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzerconfigwidget.ui b/src/plugins/clangstaticanalyzer/clangstaticanalyzerconfigwidget.ui
new file mode 100644
index 0000000000..800733a331
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerconfigwidget.ui
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ClangStaticAnalyzer::Internal::ClangStaticAnalyzerConfigWidget</class>
+ <widget class="QWidget" name="ClangStaticAnalyzer::Internal::ClangStaticAnalyzerConfigWidget">
+ <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">
+ <item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>General</string>
+ </property>
+ <layout class="QFormLayout" name="formLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Clang executable:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="Utils::PathChooser" name="clangExecutableChooser" native="true"/>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Simultaneous processes:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QSpinBox" name="simultaneousProccessesSpinBox">
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>32</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_2">
+ <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>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>183</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>Utils::PathChooser</class>
+ <extends>QWidget</extends>
+ <header location="global">utils/pathchooser.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzerconstants.h b/src/plugins/clangstaticanalyzer/clangstaticanalyzerconstants.h
new file mode 100644
index 0000000000..66b1f149fe
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerconstants.h
@@ -0,0 +1,38 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#ifndef CLANGSTATICANALYZERCONSTANTS_H
+#define CLANGSTATICANALYZERCONSTANTS_H
+
+namespace ClangStaticAnalyzer {
+namespace Constants {
+
+const char SETTINGS_ID[] = "ClangStaticAnalyzer";
+const char CLANGSTATICANALYZER_RUN_MODE[] = "ClangStaticAnalyzer.RunMode";
+
+} // Constants
+} // ClangStaticAnalyzer
+
+#endif // CLANGSTATICANALYZERCONSTANTS_H
diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnostic.cpp b/src/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnostic.cpp
new file mode 100644
index 0000000000..173e5ee596
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnostic.cpp
@@ -0,0 +1,47 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "clangstaticanalyzerdiagnostic.h"
+
+namespace ClangStaticAnalyzer {
+namespace Internal {
+
+ExplainingStep::ExplainingStep()
+ : depth(0)
+{
+}
+
+bool ExplainingStep::isValid() const
+{
+ return location.isValid() && !ranges.isEmpty() && !message.isEmpty();
+}
+
+bool Diagnostic::isValid() const
+{
+ return !description.isEmpty();
+}
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzer
diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnostic.h b/src/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnostic.h
new file mode 100644
index 0000000000..6da02dfaa9
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnostic.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#ifndef CLANGSTATICANALZYERDIAGNOSTIC_H
+#define CLANGSTATICANALZYERDIAGNOSTIC_H
+
+#include <analyzerbase/diagnosticlocation.h>
+
+#include <QList>
+#include <QMetaType>
+#include <QString>
+
+namespace ClangStaticAnalyzer {
+namespace Internal {
+
+class ExplainingStep
+{
+public:
+ ExplainingStep();
+
+ bool isValid() const;
+
+ QString message;
+ QString extendedMessage;
+ Analyzer::DiagnosticLocation location;
+ QList<Analyzer::DiagnosticLocation> ranges;
+ int depth;
+};
+
+class Diagnostic
+{
+public:
+ bool isValid() const;
+
+ QString description;
+ QString category;
+ QString type;
+ QString issueContextKind;
+ QString issueContext;
+ Analyzer::DiagnosticLocation location;
+ QList<ExplainingStep> explainingSteps;
+};
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzer
+
+Q_DECLARE_METATYPE(ClangStaticAnalyzer::Internal::Diagnostic)
+
+#endif // CLANGSTATICANALZYERDIAGNOSTIC_H
diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticmodel.cpp b/src/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticmodel.cpp
new file mode 100644
index 0000000000..fa0625891a
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticmodel.cpp
@@ -0,0 +1,357 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "clangstaticanalyzerdiagnosticmodel.h"
+
+#include "clangstaticanalyzerdiagnosticview.h"
+#include "clangstaticanalyzerprojectsettingsmanager.h"
+#include "clangstaticanalyzerutils.h"
+
+#include <projectexplorer/project.h>
+#include <projectexplorer/session.h>
+#include <utils/qtcassert.h>
+
+#include <QCoreApplication>
+#include <QFileInfo>
+
+#include <cmath>
+
+namespace ClangStaticAnalyzer {
+namespace Internal {
+
+class DiagnosticItem : public Utils::TreeItem
+{
+public:
+ DiagnosticItem(const Diagnostic &diag);
+
+ Diagnostic diagnostic() const { return m_diagnostic; }
+
+private:
+ QVariant data(int column, int role) const override;
+
+ const Diagnostic m_diagnostic;
+};
+
+class ExplainingStepItem : public Utils::TreeItem
+{
+public:
+ ExplainingStepItem(const ExplainingStep &step);
+
+private:
+ QVariant data(int column, int role) const override;
+
+ const ExplainingStep m_step;
+};
+
+ClangStaticAnalyzerDiagnosticModel::ClangStaticAnalyzerDiagnosticModel(QObject *parent)
+ : Utils::TreeModel(parent)
+{
+ setHeader(QStringList() << tr("Issue") << tr("Location"));
+}
+
+void ClangStaticAnalyzerDiagnosticModel::addDiagnostics(const QList<Diagnostic> &diagnostics)
+{
+ foreach (const Diagnostic &d, diagnostics)
+ rootItem()->appendChild(new DiagnosticItem(d));
+}
+
+QList<Diagnostic> ClangStaticAnalyzerDiagnosticModel::diagnostics() const
+{
+ QList<Diagnostic> diags;
+ foreach (const Utils::TreeItem * const item, rootItem()->children())
+ diags << static_cast<const DiagnosticItem *>(item)->diagnostic();
+ return diags;
+}
+
+static QString createDiagnosticToolTipString(const Diagnostic &diagnostic)
+{
+ typedef QPair<QString, QString> StringPair;
+ QList<StringPair> lines;
+
+ if (!diagnostic.category.isEmpty()) {
+ lines << qMakePair(
+ QCoreApplication::translate("ClangStaticAnalyzer::Diagnostic", "Category:"),
+ diagnostic.category.toHtmlEscaped());
+ }
+
+ if (!diagnostic.type.isEmpty()) {
+ lines << qMakePair(
+ QCoreApplication::translate("ClangStaticAnalyzer::Diagnostic", "Type:"),
+ diagnostic.type.toHtmlEscaped());
+ }
+
+ if (!diagnostic.issueContext.isEmpty() && !diagnostic.issueContextKind.isEmpty()) {
+ lines << qMakePair(
+ QCoreApplication::translate("ClangStaticAnalyzer::Diagnostic", "Context:"),
+ diagnostic.issueContextKind.toHtmlEscaped() + QLatin1Char(' ')
+ + diagnostic.issueContext.toHtmlEscaped());
+ }
+
+ lines << qMakePair(
+ QCoreApplication::translate("ClangStaticAnalyzer::Diagnostic", "Location:"),
+ createFullLocationString(diagnostic.location));
+
+ QString html = QLatin1String("<html>"
+ "<head>"
+ "<style>dt { font-weight:bold; } dd { font-family: monospace; }</style>\n"
+ "<body><dl>");
+
+ foreach (const StringPair &pair, lines) {
+ html += QLatin1String("<dt>");
+ html += pair.first;
+ html += QLatin1String("</dt><dd>");
+ html += pair.second;
+ html += QLatin1String("</dd>\n");
+ }
+ html += QLatin1String("</dl></body></html>");
+ return html;
+}
+
+static QString createExplainingStepToolTipString(const ExplainingStep &step)
+{
+ if (step.message == step.extendedMessage)
+ return createFullLocationString(step.location);
+
+ typedef QPair<QString, QString> StringPair;
+ QList<StringPair> lines;
+
+ if (!step.message.isEmpty()) {
+ lines << qMakePair(
+ QCoreApplication::translate("ClangStaticAnalyzer::ExplainingStep", "Message:"),
+ step.message.toHtmlEscaped());
+ }
+ if (!step.extendedMessage.isEmpty()) {
+ lines << qMakePair(
+ QCoreApplication::translate("ClangStaticAnalyzer::ExplainingStep", "Extended Message:"),
+ step.extendedMessage.toHtmlEscaped());
+ }
+
+ lines << qMakePair(
+ QCoreApplication::translate("ClangStaticAnalyzer::ExplainingStep", "Location:"),
+ createFullLocationString(step.location));
+
+ QString html = QLatin1String("<html>"
+ "<head>"
+ "<style>dt { font-weight:bold; } dd { font-family: monospace; }</style>\n"
+ "<body><dl>");
+
+ foreach (const StringPair &pair, lines) {
+ html += QLatin1String("<dt>");
+ html += pair.first;
+ html += QLatin1String("</dt><dd>");
+ html += pair.second;
+ html += QLatin1String("</dd>\n");
+ }
+ html += QLatin1String("</dl></body></html>");
+ return html;
+}
+
+static QString createLocationString(const Analyzer::DiagnosticLocation &location)
+{
+ const QString filePath = location.filePath;
+ const QString lineNumber = QString::number(location.line);
+ const QString fileAndLine = filePath + QLatin1Char(':') + lineNumber;
+ return QLatin1String("in ") + fileAndLine;
+}
+
+static QString createExplainingStepNumberString(int number)
+{
+ const int fieldWidth = 2;
+ return QString::fromLatin1("%1:").arg(number, fieldWidth);
+}
+
+static QString createExplainingStepString(const ExplainingStep &explainingStep, int number)
+{
+ return createExplainingStepNumberString(number)
+ + QLatin1Char(' ')
+ + explainingStep.extendedMessage
+ + QLatin1Char(' ')
+ + createLocationString(explainingStep.location);
+}
+
+static QString fullText(const Diagnostic &diagnostic)
+{
+ // Summary.
+ QString text = diagnostic.category + QLatin1String(": ") + diagnostic.type;
+ if (diagnostic.type != diagnostic.description)
+ text += QLatin1String(": ") + diagnostic.description;
+ text += QLatin1Char('\n');
+
+ // Explaining steps.
+ int explainingStepNumber = 1;
+ foreach (const ExplainingStep &explainingStep, diagnostic.explainingSteps) {
+ text += createExplainingStepString(explainingStep, explainingStepNumber++)
+ + QLatin1Char('\n');
+ }
+
+ text.chop(1); // Trailing newline.
+ return text;
+}
+
+
+DiagnosticItem::DiagnosticItem(const Diagnostic &diag) : m_diagnostic(diag)
+{
+ // Don't show explaining steps if they add no information.
+ if (diag.explainingSteps.count() == 1) {
+ const ExplainingStep &step = diag.explainingSteps.first();
+ if (step.message == diag.description && step.location == diag.location)
+ return;
+ }
+
+ foreach (const ExplainingStep &s, diag.explainingSteps)
+ appendChild(new ExplainingStepItem(s));
+}
+
+QVariant locationData(int role, const Analyzer::DiagnosticLocation &location)
+{
+ switch (role) {
+ case Analyzer::DetailedErrorView::LocationRole:
+ return QVariant::fromValue(location);
+ case Qt::ToolTipRole:
+ return location.filePath.isEmpty() ? QVariant() : QVariant(location.filePath);
+ default:
+ return QVariant();
+ }
+}
+
+QVariant DiagnosticItem::data(int column, int role) const
+{
+ if (column == Analyzer::DetailedErrorView::LocationColumn)
+ return locationData(role, m_diagnostic.location);
+
+ // DiagnosticColumn
+ switch (role) {
+ case Analyzer::DetailedErrorView::FullTextRole:
+ return fullText(m_diagnostic);
+ case ClangStaticAnalyzerDiagnosticModel::DiagnosticRole:
+ return QVariant::fromValue(m_diagnostic);
+ case Qt::DisplayRole:
+ return m_diagnostic.description;
+ case Qt::ToolTipRole:
+ return createDiagnosticToolTipString(m_diagnostic);
+ default:
+ return QVariant();
+ }
+}
+
+ExplainingStepItem::ExplainingStepItem(const ExplainingStep &step) : m_step(step)
+{
+}
+
+QVariant ExplainingStepItem::data(int column, int role) const
+{
+ if (column == Analyzer::DetailedErrorView::LocationColumn)
+ return locationData(role, m_step.location);
+
+ // DiagnosticColumn
+ switch (role) {
+ case Analyzer::DetailedErrorView::FullTextRole:
+ return fullText(static_cast<DiagnosticItem *>(parent())->diagnostic());
+ case ClangStaticAnalyzerDiagnosticModel::DiagnosticRole:
+ return QVariant::fromValue(static_cast<DiagnosticItem *>(parent())->diagnostic());
+ case Qt::DisplayRole: {
+ const int row = parent()->children().indexOf(const_cast<ExplainingStepItem *>(this)) + 1;
+ const int padding = static_cast<int>(std::log10(parent()->rowCount()))
+ - static_cast<int>(std::log10(row));
+ return QString::fromLatin1("%1%2: %3")
+ .arg(QString(padding, QLatin1Char(' ')))
+ .arg(row)
+ .arg(m_step.message);
+ }
+ case Qt::ToolTipRole:
+ return createExplainingStepToolTipString(m_step);
+ default:
+ 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
+{
+ if (sourceParent.isValid())
+ return true;
+ 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/src/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticmodel.h b/src/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticmodel.h
new file mode 100644
index 0000000000..9c93fb9f77
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticmodel.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#ifndef CLANGSTATICANALYZERDIAGNOSTICMODEL_H
+#define CLANGSTATICANALYZERDIAGNOSTICMODEL_H
+
+#include "clangstaticanalyzerdiagnostic.h"
+#include "clangstaticanalyzerprojectsettings.h"
+
+#include <analyzerbase/detailederrorview.h>
+#include <utils/fileutils.h>
+#include <utils/treemodel.h>
+
+#include <QPointer>
+#include <QSortFilterProxyModel>
+
+namespace ProjectExplorer { class Project; }
+
+namespace ClangStaticAnalyzer {
+namespace Internal {
+
+class ClangStaticAnalyzerDiagnosticModel : public Utils::TreeModel
+{
+ Q_OBJECT
+
+public:
+ ClangStaticAnalyzerDiagnosticModel(QObject *parent = 0);
+
+ void addDiagnostics(const QList<Diagnostic> &diagnostics);
+ QList<Diagnostic> diagnostics() const;
+
+ enum ItemRole {
+ DiagnosticRole = Analyzer::DetailedErrorView::FullTextRole + 1
+ };
+};
+
+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
+
+#endif // CLANGSTATICANALYZERDIAGNOSTICMODEL_H
diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticview.cpp b/src/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticview.cpp
new file mode 100644
index 0000000000..69fb87a3bb
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticview.cpp
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "clangstaticanalyzerdiagnosticview.h"
+
+#include "clangstaticanalyzerdiagnosticmodel.h"
+#include "clangstaticanalyzerprojectsettings.h"
+#include "clangstaticanalyzerprojectsettingsmanager.h"
+#include "clangstaticanalyzerutils.h"
+
+#include <utils/fileutils.h>
+#include <utils/qtcassert.h>
+
+#include <QAction>
+#include <QDebug>
+
+using namespace Analyzer;
+
+namespace ClangStaticAnalyzer {
+namespace Internal {
+
+ClangStaticAnalyzerDiagnosticView::ClangStaticAnalyzerDiagnosticView(QWidget *parent)
+ : Analyzer::DetailedErrorView(parent)
+{
+ m_suppressAction = new QAction(tr("Suppress this diagnostic"), this);
+ connect(m_suppressAction, &QAction::triggered, [this](bool) { suppressCurrentDiagnostic(); });
+}
+
+void ClangStaticAnalyzerDiagnosticView::suppressCurrentDiagnostic()
+{
+ const QModelIndexList indexes = selectionModel()->selectedRows();
+ QTC_ASSERT(indexes.count() == 1, return);
+ const Diagnostic diag = model()->data(indexes.first(),
+ ClangStaticAnalyzerDiagnosticModel::DiagnosticRole)
+ .value<Diagnostic>();
+ 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
+} // namespace ClangStaticAnalyzer
diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticview.h b/src/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticview.h
new file mode 100644
index 0000000000..392294fd86
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticview.h
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#ifndef CLANGSTATICANALYZERDIAGNOSTICVIEW_H
+#define CLANGSTATICANALYZERDIAGNOSTICVIEW_H
+
+#include <analyzerbase/detailederrorview.h>
+
+namespace ClangStaticAnalyzer {
+namespace Internal {
+
+class ClangStaticAnalyzerDiagnosticView : public Analyzer::DetailedErrorView
+{
+ Q_OBJECT
+
+public:
+ ClangStaticAnalyzerDiagnosticView(QWidget *parent = 0);
+
+private:
+ void suppressCurrentDiagnostic();
+
+ QList<QAction *> customActions() const;
+
+ QAction *m_suppressAction;
+};
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzer
+
+#endif // CLANGSTATICANALYZERDIAGNOSTICVIEW_H
diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzerlogfilereader.cpp b/src/plugins/clangstaticanalyzer/clangstaticanalyzerlogfilereader.cpp
new file mode 100644
index 0000000000..dd0727864f
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerlogfilereader.cpp
@@ -0,0 +1,381 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "clangstaticanalyzerlogfilereader.h"
+
+#include <QDebug>
+#include <QObject>
+#include <QFile>
+#include <QFileInfo>
+#include <QXmlStreamReader>
+
+#include <utils/qtcassert.h>
+
+namespace ClangStaticAnalyzer {
+namespace Internal {
+
+class ClangStaticAnalyzerLogFileReader
+{
+public:
+ ClangStaticAnalyzerLogFileReader(const QString &filePath);
+
+ QXmlStreamReader::Error read();
+
+ // Output
+ QString clangVersion() const;
+ QStringList files() const;
+ QList<Diagnostic> diagnostics() const;
+
+private:
+ void readPlist();
+ void readTopLevelDict();
+ void readDiagnosticsArray();
+ void readDiagnosticsDict();
+ QList<ExplainingStep> readPathArray();
+ ExplainingStep readPathDict();
+ Analyzer::DiagnosticLocation readLocationDict(bool elementIsRead = false);
+ QList<Analyzer::DiagnosticLocation> readRangesArray();
+
+ QString readString();
+ QStringList readStringArray();
+ int readInteger(bool *convertedSuccessfully);
+
+private:
+ QString m_filePath;
+ QXmlStreamReader m_xml;
+
+ QString m_clangVersion;
+ QStringList m_referencedFiles;
+ QList<Diagnostic> m_diagnostics;
+};
+
+QList<Diagnostic> LogFileReader::read(const QString &filePath, QString *errorMessage)
+{
+ const QList<Diagnostic> emptyList;
+
+ // Check file path
+ QFileInfo fi(filePath);
+ if (!fi.exists() || !fi.isReadable()) {
+ if (errorMessage) {
+ *errorMessage = QObject::tr("File \"%1\" does not exist or is not readable.")
+ .arg(filePath);
+ }
+ return emptyList;
+ }
+
+ // Read
+ ClangStaticAnalyzerLogFileReader reader(filePath);
+ const QXmlStreamReader::Error error = reader.read();
+
+ // Return diagnostics
+ switch (error) {
+ case QXmlStreamReader::NoError:
+ return reader.diagnostics();
+
+ // Handle errors
+ case QXmlStreamReader::UnexpectedElementError:
+ if (errorMessage) {
+ *errorMessage = QObject::tr("Could not read file \"%1\": UnexpectedElementError.")
+ .arg(filePath);
+ } // fall-through
+ case QXmlStreamReader::CustomError:
+ if (errorMessage) {
+ *errorMessage = QObject::tr("Could not read file \"%1\": CustomError.")
+ .arg(filePath);
+ } // fall-through
+ case QXmlStreamReader::NotWellFormedError:
+ if (errorMessage) {
+ *errorMessage = QObject::tr("Could not read file \"%1\": NotWellFormedError.")
+ .arg(filePath);
+ } // fall-through
+ case QXmlStreamReader::PrematureEndOfDocumentError:
+ if (errorMessage) {
+ *errorMessage = QObject::tr("Could not read file \"%1\": PrematureEndOfDocumentError.")
+ .arg(filePath);
+ } // fall-through
+ default:
+ return emptyList;
+ }
+}
+
+ClangStaticAnalyzerLogFileReader::ClangStaticAnalyzerLogFileReader(const QString &filePath)
+ : m_filePath(filePath)
+{
+}
+
+QXmlStreamReader::Error ClangStaticAnalyzerLogFileReader::read()
+{
+ QTC_ASSERT(!m_filePath.isEmpty(), return QXmlStreamReader::CustomError);
+ QFile file(m_filePath);
+ QTC_ASSERT(file.open(QIODevice::ReadOnly | QIODevice::Text),
+ return QXmlStreamReader::CustomError);
+
+ m_xml.setDevice(&file);
+ readPlist();
+
+ // If file is empty, m_xml.error() == QXmlStreamReader::PrematureEndOfDocumentError
+ return m_xml.error();
+}
+
+QString ClangStaticAnalyzerLogFileReader::clangVersion() const
+{
+ return m_clangVersion;
+}
+
+QStringList ClangStaticAnalyzerLogFileReader::files() const
+{
+ return m_referencedFiles;
+}
+
+QList<Diagnostic> ClangStaticAnalyzerLogFileReader::diagnostics() const
+{
+ return m_diagnostics;
+}
+
+void ClangStaticAnalyzerLogFileReader::readPlist()
+{
+ if (m_xml.readNextStartElement()) {
+ if (m_xml.name() == QLatin1String("plist")) {
+ if (m_xml.attributes().value(QLatin1String("version")) == QLatin1String("1.0"))
+ readTopLevelDict();
+ } else {
+ m_xml.raiseError(QObject::tr("File is not a plist version 1.0 file."));
+ }
+ }
+}
+
+void ClangStaticAnalyzerLogFileReader::readTopLevelDict()
+{
+ QTC_ASSERT(m_xml.isStartElement() && m_xml.name() == QLatin1String("plist"), return);
+ QTC_ASSERT(m_xml.readNextStartElement() && m_xml.name() == QLatin1String("dict"), return);
+
+ while (m_xml.readNextStartElement()) {
+ if (m_xml.name() == QLatin1String("key")) {
+ const QString key = m_xml.readElementText();
+ if (key == QLatin1String("clang_version"))
+ m_clangVersion = readString();
+ else if (key == QLatin1String("files"))
+ m_referencedFiles = readStringArray();
+ else if (key == QLatin1String("diagnostics"))
+ readDiagnosticsArray();
+ } else {
+ m_xml.skipCurrentElement();
+ }
+ }
+}
+
+void ClangStaticAnalyzerLogFileReader::readDiagnosticsArray()
+{
+ if (m_xml.readNextStartElement() && m_xml.name() == QLatin1String("array")) {
+ while (m_xml.readNextStartElement() && m_xml.name() == QLatin1String("dict"))
+ readDiagnosticsDict();
+ }
+}
+
+void ClangStaticAnalyzerLogFileReader::readDiagnosticsDict()
+{
+ QTC_ASSERT(m_xml.isStartElement() && m_xml.name() == QLatin1String("dict"), return);
+
+ Diagnostic diagnostic;
+
+ while (m_xml.readNextStartElement()) {
+ if (m_xml.name() == QLatin1String("key")) {
+ const QString key = m_xml.readElementText();
+ if (key == QLatin1String("path"))
+ diagnostic.explainingSteps = readPathArray();
+ else if (key == QLatin1String("description"))
+ diagnostic.description = readString();
+ else if (key == QLatin1String("category"))
+ diagnostic.category = readString();
+ else if (key == QLatin1String("type"))
+ diagnostic.type = readString();
+ else if (key == QLatin1String("issue_context_kind"))
+ diagnostic.issueContextKind = readString();
+ else if (key == QLatin1String("issue_context"))
+ diagnostic.issueContext = readString();
+ else if (key == QLatin1String("location"))
+ diagnostic.location = readLocationDict();
+ } else {
+ m_xml.skipCurrentElement();
+ }
+ }
+
+ m_diagnostics << diagnostic;
+}
+
+QList<ExplainingStep> ClangStaticAnalyzerLogFileReader::readPathArray()
+{
+ QList<ExplainingStep> result;
+
+ if (m_xml.readNextStartElement() && m_xml.name() == QLatin1String("array")) {
+ while (m_xml.readNextStartElement() && m_xml.name() == QLatin1String("dict")) {
+ const ExplainingStep step = readPathDict();
+ if (step.isValid())
+ result << step;
+ }
+ }
+
+ return result;
+}
+
+ExplainingStep ClangStaticAnalyzerLogFileReader::readPathDict()
+{
+ ExplainingStep explainingStep;
+
+ QTC_ASSERT(m_xml.isStartElement() && m_xml.name() == QLatin1String("dict"),
+ return explainingStep);
+
+ // We are interested only in dict entries an kind=event type
+ if (m_xml.readNextStartElement()) {
+ if (m_xml.name() == QLatin1String("key")) {
+ const QString key = m_xml.readElementText();
+ QTC_ASSERT(key == QLatin1String("kind"), return explainingStep);
+ const QString kind = readString();
+ if (kind != QLatin1String("event")) {
+ m_xml.skipCurrentElement();
+ return explainingStep;
+ }
+ }
+ }
+
+ bool depthOk = false;
+
+ while (m_xml.readNextStartElement()) {
+ if (m_xml.name() == QLatin1String("key")) {
+ const QString key = m_xml.readElementText();
+ if (key == QLatin1String("location"))
+ explainingStep.location = readLocationDict();
+ else if (key == QLatin1String("ranges"))
+ explainingStep.ranges = readRangesArray();
+ else if (key == QLatin1String("depth"))
+ explainingStep.depth = readInteger(&depthOk);
+ else if (key == QLatin1String("message"))
+ explainingStep.message = readString();
+ else if (key == QLatin1String("extended_message"))
+ explainingStep.extendedMessage = readString();
+ } else {
+ m_xml.skipCurrentElement();
+ }
+ }
+
+ QTC_CHECK(depthOk);
+ return explainingStep;
+}
+
+Analyzer::DiagnosticLocation ClangStaticAnalyzerLogFileReader::readLocationDict(bool elementIsRead)
+{
+ Analyzer::DiagnosticLocation location;
+ if (elementIsRead) {
+ QTC_ASSERT(m_xml.isStartElement() && m_xml.name() == QLatin1String("dict"),
+ return location);
+ } else {
+ QTC_ASSERT(m_xml.readNextStartElement() && m_xml.name() == QLatin1String("dict"),
+ return location);
+ }
+
+ int line = 0;
+ int column = 0;
+ int fileIndex = 0;
+ bool lineOk = false, columnOk = false, fileIndexOk = false;
+
+ // Collect values
+ while (m_xml.readNextStartElement()) {
+ if (m_xml.name() == QLatin1String("key")) {
+ const QString keyName = m_xml.readElementText();
+ if (keyName == QLatin1String("line"))
+ line = readInteger(&lineOk);
+ else if (keyName == QLatin1String("col"))
+ column = readInteger(&columnOk);
+ else if (keyName == QLatin1String("file"))
+ fileIndex = readInteger(&fileIndexOk);
+ } else {
+ m_xml.skipCurrentElement();
+ }
+ }
+
+ if (lineOk && columnOk && fileIndexOk) {
+ QTC_ASSERT(fileIndex < m_referencedFiles.size(), return location);
+ location = Analyzer::DiagnosticLocation(m_referencedFiles.at(fileIndex), line, column);
+ }
+ return location;
+}
+
+QList<Analyzer::DiagnosticLocation> ClangStaticAnalyzerLogFileReader::readRangesArray()
+{
+ QList<Analyzer::DiagnosticLocation> result;
+
+ // It's an array of arrays...
+ QTC_ASSERT(m_xml.readNextStartElement() && m_xml.name() == QLatin1String("array"),
+ return result);
+ QTC_ASSERT(m_xml.readNextStartElement() && m_xml.name() == QLatin1String("array"),
+ return result);
+
+ while (m_xml.readNextStartElement() && m_xml.name() == QLatin1String("dict"))
+ result << readLocationDict(true);
+
+ m_xml.skipCurrentElement(); // Laeve outer array
+ return result;
+}
+
+QString ClangStaticAnalyzerLogFileReader::readString()
+{
+ if (m_xml.readNextStartElement() && m_xml.name() == QLatin1String("string"))
+ return m_xml.readElementText();
+
+ m_xml.raiseError(QObject::tr("Expected a string element."));
+ return QString();
+}
+
+QStringList ClangStaticAnalyzerLogFileReader::readStringArray()
+{
+ if (m_xml.readNextStartElement() && m_xml.name() == QLatin1String("array")) {
+ QStringList result;
+ while (m_xml.readNextStartElement() && m_xml.name() == QLatin1String("string")) {
+ const QString string = m_xml.readElementText();
+ if (!string.isEmpty())
+ result << string;
+ }
+ return result;
+ }
+
+ m_xml.raiseError(QObject::tr("Expected an array element."));
+ return QStringList();
+}
+
+int ClangStaticAnalyzerLogFileReader::readInteger(bool *convertedSuccessfully)
+{
+ if (m_xml.readNextStartElement() && m_xml.name() == QLatin1String("integer")) {
+ const QString contents = m_xml.readElementText();
+ return contents.toInt(convertedSuccessfully);
+ }
+
+ m_xml.raiseError(QObject::tr("Expected an integer element."));
+ if (convertedSuccessfully)
+ *convertedSuccessfully = false;
+ return -1;
+}
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzer
diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzerlogfilereader.h b/src/plugins/clangstaticanalyzer/clangstaticanalyzerlogfilereader.h
new file mode 100644
index 0000000000..8b05ada7aa
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerlogfilereader.h
@@ -0,0 +1,45 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#ifndef CLANGSTATICANALYZERLOGFILEREADER_H
+#define CLANGSTATICANALYZERLOGFILEREADER_H
+
+#include "clangstaticanalyzerdiagnostic.h"
+
+#include <QList>
+
+namespace ClangStaticAnalyzer {
+namespace Internal {
+
+class LogFileReader
+{
+public:
+ static QList<Diagnostic> read(const QString &filePath, QString *errorMessage);
+};
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzer
+
+#endif // CLANGSTATICANALYZERLOGFILEREADER_H
diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzerplugin.cpp b/src/plugins/clangstaticanalyzer/clangstaticanalyzerplugin.cpp
new file mode 100644
index 0000000000..eb9683e3d4
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerplugin.cpp
@@ -0,0 +1,184 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "clangstaticanalyzerplugin.h"
+
+#include "clangstaticanalyzerconfigwidget.h"
+#include "clangstaticanalyzerconstants.h"
+#include "clangstaticanalyzerprojectsettingswidget.h"
+#include "clangstaticanalyzerruncontrolfactory.h"
+#include "clangstaticanalyzertool.h"
+
+#ifdef WITH_TESTS
+#include "clangstaticanalyzerunittests.h"
+#endif
+
+#include <analyzerbase/analyzermanager.h>
+#include <coreplugin/icore.h>
+#include <coreplugin/icontext.h>
+#include <coreplugin/actionmanager/actionmanager.h>
+#include <coreplugin/actionmanager/command.h>
+#include <coreplugin/actionmanager/actioncontainer.h>
+#include <coreplugin/coreconstants.h>
+#include <coreplugin/dialogs/ioptionspage.h>
+#include <projectexplorer/projectpanelfactory.h>
+
+#include <QAction>
+#include <QDebug>
+#include <QMainWindow>
+#include <QMessageBox>
+#include <QMenu>
+
+#include <QtPlugin>
+
+using namespace Analyzer;
+
+namespace ClangStaticAnalyzer {
+namespace Internal {
+
+class ClangStaticAnalyzerOptionsPage : public Core::IOptionsPage
+{
+public:
+ explicit ClangStaticAnalyzerOptionsPage()
+ {
+ setId("Analyzer.ClangStaticAnalyzer.Settings"); // TODO: Get it from "clangstaticanalyzersettings.h"
+ setDisplayName(QCoreApplication::translate(
+ "ClangStaticAnalyzer::Internal::ClangStaticAnalyzerOptionsPage",
+ "Clang Static Analyzer"));
+ setCategory("T.Analyzer");
+ setDisplayCategory(QCoreApplication::translate("Analyzer", "Analyzer"));
+ setCategoryIcon(QLatin1String(":/images/analyzer_category.png"));
+ }
+
+ QWidget *widget()
+ {
+ if (!m_widget)
+ m_widget = new ClangStaticAnalyzerConfigWidget(ClangStaticAnalyzerSettings::instance());
+ return m_widget;
+ }
+
+ void apply()
+ {
+ ClangStaticAnalyzerSettings::instance()->writeSettings();
+ }
+
+ void finish()
+ {
+ delete m_widget;
+ }
+
+private:
+ QPointer<QWidget> m_widget;
+};
+
+ClangStaticAnalyzerPlugin::ClangStaticAnalyzerPlugin()
+{
+ // Create your members
+}
+
+ClangStaticAnalyzerPlugin::~ClangStaticAnalyzerPlugin()
+{
+ // Unregister objects from the plugin manager's object pool
+ // Delete members
+}
+
+bool ClangStaticAnalyzerPlugin::initialize(const QStringList &arguments, QString *errorString)
+{
+ // Register objects in the plugin manager's object pool
+ // Load settings
+ // Add actions to menus
+ // Connect to other plugins' signals
+ // 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);
+ return initializeEnterpriseFeatures(arguments, errorString);
+}
+
+bool ClangStaticAnalyzerPlugin::initializeEnterpriseFeatures(const QStringList &arguments,
+ QString *errorString)
+{
+ Q_UNUSED(arguments);
+ Q_UNUSED(errorString);
+
+ 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 &,
+ ProjectExplorer::RunConfiguration *runConfiguration, Core::Id runMode) {
+ return tool->createRunControl(runConfiguration, runMode);
+ };
+
+ const QString toolTip = tr("Clang Static Analyzer uses the analyzer from the clang project "
+ "to find bugs.");
+
+ auto action = new AnalyzerAction(this);
+ action->setRunMode(Constants::CLANGSTATICANALYZER_RUN_MODE);
+ action->setToolId(ClangStaticAnalyzerToolId);
+ action->setActionId("ClangStaticAnalyzer");
+ action->setWidgetCreator(widgetCreator);
+ action->setRunControlCreator(runControlCreator);
+ action->setCustomToolStarter([tool] { tool->startTool(); });
+ action->setText(tr("Clang Static Analyzer"));
+ action->setToolTip(toolTip);
+ action->setMenuGroup(Analyzer::Constants::G_ANALYZER_TOOLS);
+ action->setEnabled(false);
+ AnalyzerManager::addAction(action);
+
+ return true;
+}
+
+void ClangStaticAnalyzerPlugin::extensionsInitialized()
+{
+ // Retrieve objects from the plugin manager's object pool
+ // In the extensionsInitialized method, a plugin can be sure that all
+ // plugins that depend on it are completely initialized.
+}
+
+ExtensionSystem::IPlugin::ShutdownFlag ClangStaticAnalyzerPlugin::aboutToShutdown()
+{
+ // Save settings
+ // Disconnect from signals that are not needed during shutdown
+ // Hide UI (if you add UI that is not in the main window directly)
+ return SynchronousShutdown;
+}
+
+QList<QObject *> ClangStaticAnalyzerPlugin::createTestObjects() const
+{
+ QList<QObject *> tests;
+#ifdef WITH_TESTS
+ tests << new ClangStaticAnalyzerUnitTests(m_analyzerTool);
+#endif
+ return tests;
+}
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzerPlugin
diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzerplugin.h b/src/plugins/clangstaticanalyzer/clangstaticanalyzerplugin.h
new file mode 100644
index 0000000000..b663a16487
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerplugin.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#ifndef CLANGSTATICANALYZERPLUGIN_H
+#define CLANGSTATICANALYZERPLUGIN_H
+
+#include <extensionsystem/iplugin.h>
+
+namespace ClangStaticAnalyzer {
+namespace Internal {
+
+class ClangStaticAnalyzerTool;
+class ClangStaticAnalyzerSettings;
+
+class ClangStaticAnalyzerPlugin : public ExtensionSystem::IPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "ClangStaticAnalyzer.json")
+
+public:
+ ClangStaticAnalyzerPlugin();
+ ~ClangStaticAnalyzerPlugin();
+
+ bool initialize(const QStringList &arguments, QString *errorString);
+ bool initializeEnterpriseFeatures(const QStringList &arguments, QString *errorString);
+ void extensionsInitialized();
+ ShutdownFlag aboutToShutdown();
+
+private:
+ QList<QObject *> createTestObjects() const;
+
+ ClangStaticAnalyzerTool *m_analyzerTool;
+};
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzerPlugin
+
+#endif // CLANGSTATICANALYZERPLUGIN_H
+
diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettings.cpp b/src/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettings.cpp
new file mode 100644
index 0000000000..4d37154c85
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettings.cpp
@@ -0,0 +1,145 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#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/src/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettings.h b/src/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettings.h
new file mode 100644
index 0000000000..f30298b69f
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettings.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#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/src/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingsmanager.cpp b/src/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingsmanager.cpp
new file mode 100644
index 0000000000..c0b456345b
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingsmanager.cpp
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#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/src/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingsmanager.h b/src/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingsmanager.h
new file mode 100644
index 0000000000..a588a7824d
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingsmanager.h
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#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/src/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingswidget.cpp b/src/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingswidget.cpp
new file mode 100644
index 0000000000..3ec14f7d29
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingswidget.cpp
@@ -0,0 +1,168 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#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/src/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingswidget.h b/src/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingswidget.h
new file mode 100644
index 0000000000..b68003b25c
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingswidget.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#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/src/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingswidget.ui b/src/plugins/clangstaticanalyzer/clangstaticanalyzerprojectsettingswidget.ui
new file mode 100644
index 0000000000..c131bbe0c2
--- /dev/null
+++ b/src/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/src/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.cpp b/src/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.cpp
new file mode 100644
index 0000000000..35652728c4
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.cpp
@@ -0,0 +1,511 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "clangstaticanalyzerruncontrol.h"
+
+#include "clangstaticanalyzerlogfilereader.h"
+#include "clangstaticanalyzerrunner.h"
+#include "clangstaticanalyzersettings.h"
+#include "clangstaticanalyzerutils.h"
+
+#include <analyzerbase/analyzermanager.h>
+#include <analyzerbase/analyzerutils.h>
+
+#include <clangcodemodel/clangutils.h>
+
+#include <coreplugin/progressmanager/futureprogress.h>
+#include <coreplugin/progressmanager/progressmanager.h>
+
+#include <cpptools/compileroptionsbuilder.h>
+#include <cpptools/cppmodelmanager.h>
+#include <cpptools/cppprojectfile.h>
+#include <cpptools/projectinfo.h>
+
+#include <projectexplorer/abi.h>
+#include <projectexplorer/buildconfiguration.h>
+#include <projectexplorer/kitinformation.h>
+#include <projectexplorer/project.h>
+#include <projectexplorer/runconfiguration.h>
+#include <projectexplorer/target.h>
+#include <projectexplorer/taskhub.h>
+#include <projectexplorer/toolchain.h>
+
+#include <utils/algorithm.h>
+
+#include <QLoggingCategory>
+#include <QTemporaryDir>
+
+using namespace CppTools;
+using namespace ProjectExplorer;
+
+static Q_LOGGING_CATEGORY(LOG, "qtc.clangstaticanalyzer.runcontrol")
+
+namespace ClangStaticAnalyzer {
+namespace Internal {
+
+ClangStaticAnalyzerRunControl::ClangStaticAnalyzerRunControl(
+ RunConfiguration *runConfiguration,
+ Core::Id runMode,
+ const ProjectInfo &projectInfo)
+ : AnalyzerRunControl(runConfiguration, runMode)
+ , m_projectInfo(projectInfo)
+ , m_wordWidth(runConfiguration->abi().wordWidth())
+ , m_initialFilesToProcessSize(0)
+ , m_filesAnalyzed(0)
+ , m_filesNotAnalyzed(0)
+{
+ Target *target = runConfiguration->target();
+ BuildConfiguration *buildConfiguration = target->activeBuildConfiguration();
+ QTC_ASSERT(buildConfiguration, return);
+ m_environment = buildConfiguration->environment();
+}
+
+static void prependWordWidthArgumentIfNotIncluded(QStringList *arguments, unsigned char wordWidth)
+{
+ QTC_ASSERT(arguments, return);
+
+ const QString m64Argument = QLatin1String("-m64");
+ const QString m32Argument = QLatin1String("-m32");
+
+ const QString argument = wordWidth == 64 ? m64Argument : m32Argument;
+ if (!arguments->contains(argument))
+ arguments->prepend(argument);
+
+ QTC_CHECK(!arguments->contains(m32Argument) || !arguments->contains(m64Argument));
+}
+
+// Removes (1) filePath (2) -o <somePath>.
+// Adds -m64/-m32 argument if not already included.
+static QStringList tweakedArguments(const QString &filePath,
+ const QStringList &arguments,
+ unsigned char wordWidth)
+{
+ QStringList newArguments;
+
+ bool skip = false;
+ foreach (const QString &argument, arguments) {
+ if (skip) {
+ skip = false;
+ continue;
+ } else if (argument == QLatin1String("-o")) {
+ skip = true;
+ continue;
+ } else if (QDir::fromNativeSeparators(argument) == filePath) {
+ continue; // TODO: Let it in?
+ }
+
+ newArguments << argument;
+ }
+ QTC_CHECK(skip == false);
+
+ prependWordWidthArgumentIfNotIncluded(&newArguments, wordWidth);
+
+ return newArguments;
+}
+
+static QString createLanguageOptionMsvc(ProjectFile::Kind fileKind)
+{
+ switch (fileKind) {
+ case ProjectFile::CHeader:
+ case ProjectFile::CSource:
+ return QLatin1String("/TC");
+ break;
+ case ProjectFile::CXXHeader:
+ case ProjectFile::CXXSource:
+ return QLatin1String("/TP");
+ break;
+ default:
+ break;
+ }
+ return QString();
+}
+
+class ClangStaticAnalyzerOptionsBuilder : public CompilerOptionsBuilder
+{
+public:
+ static QStringList build(const CppTools::ProjectPart &projectPart,
+ CppTools::ProjectFile::Kind fileKind,
+ unsigned char wordWidth)
+ {
+ ClangStaticAnalyzerOptionsBuilder optionsBuilder(projectPart);
+ optionsBuilder.addLanguageOption(fileKind);
+ optionsBuilder.addOptionsForLanguage(false);
+
+ // In gcc headers, lots of built-ins are referenced that clang does not understand.
+ // Therefore, prevent the inclusion of the header that references them. Of course, this
+ // will break if code actually requires stuff from there, but that should be the less common
+ // case.
+ const Core::Id type = projectPart.toolchainType;
+ if (type == ProjectExplorer::Constants::MINGW_TOOLCHAIN_TYPEID
+ || type == ProjectExplorer::Constants::GCC_TOOLCHAIN_TYPEID)
+ optionsBuilder.addDefine("#define _X86INTRIN_H_INCLUDED\n");
+
+ optionsBuilder.addToolchainAndProjectDefines();
+ optionsBuilder.addHeaderPathOptions();
+
+ if (type == ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID)
+ optionsBuilder.add(QLatin1String("/EHsc")); // clang-cl does not understand exceptions
+ else
+ optionsBuilder.add(QLatin1String("-fPIC")); // TODO: Remove?
+
+ QStringList options = optionsBuilder.options();
+ prependWordWidthArgumentIfNotIncluded(&options, wordWidth);
+ return options;
+ }
+
+private:
+ ClangStaticAnalyzerOptionsBuilder(const CppTools::ProjectPart &projectPart)
+ : CompilerOptionsBuilder(projectPart)
+ , m_isMsvcToolchain(m_projectPart.toolchainType == ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID)
+ {
+ }
+
+ void addLanguageOption(ProjectFile::Kind fileKind) override
+ {
+ if (m_isMsvcToolchain)
+ add(createLanguageOptionMsvc(fileKind));
+ else
+ CompilerOptionsBuilder::addLanguageOption(fileKind);
+ }
+
+ void addOptionsForLanguage(bool checkForBorlandExtensions) override
+ {
+ if (m_isMsvcToolchain)
+ return;
+ CompilerOptionsBuilder::addOptionsForLanguage(checkForBorlandExtensions);
+ }
+
+ QString includeOption() const override
+ {
+ if (m_isMsvcToolchain)
+ return QLatin1String("/I");
+ return CompilerOptionsBuilder::includeOption();
+ }
+
+ QString defineOption() const override
+ {
+ if (m_isMsvcToolchain)
+ return QLatin1String("/D");
+ return CompilerOptionsBuilder::defineOption();
+ }
+
+private:
+ bool m_isMsvcToolchain;
+};
+
+static AnalyzeUnits unitsToAnalyzeFromCompilerCallData(
+ const ProjectInfo::CompilerCallData &compilerCallData,
+ unsigned char wordWidth)
+{
+ qCDebug(LOG) << "Taking arguments for analyzing from CompilerCallData.";
+
+ AnalyzeUnits unitsToAnalyze;
+
+ QHashIterator<QString, QList<QStringList> > it(compilerCallData);
+ while (it.hasNext()) {
+ it.next();
+ const QString file = it.key();
+ const QList<QStringList> compilerCalls = it.value();
+ foreach (const QStringList &options, compilerCalls) {
+ const QStringList arguments = tweakedArguments(file, options, wordWidth);
+ unitsToAnalyze << AnalyzeUnit(file, arguments);
+ }
+ }
+
+ return unitsToAnalyze;
+}
+
+static AnalyzeUnits unitsToAnalyzeFromProjectParts(const QList<ProjectPart::Ptr> projectParts,
+ unsigned char wordWidth)
+{
+ qCDebug(LOG) << "Taking arguments for analyzing from ProjectParts.";
+
+ AnalyzeUnits unitsToAnalyze;
+
+ foreach (const ProjectPart::Ptr &projectPart, projectParts) {
+ if (!projectPart->selectedForBuilding)
+ continue;
+
+ foreach (const ProjectFile &file, projectPart->files) {
+ if (file.path == CppModelManager::configurationFileName())
+ continue;
+ QTC_CHECK(file.kind != ProjectFile::Unclassified);
+ if (ProjectFile::isSource(file.kind)) {
+ const QStringList arguments
+ = ClangStaticAnalyzerOptionsBuilder::build(*projectPart.data(),
+ file.kind,
+ wordWidth);
+ unitsToAnalyze << AnalyzeUnit(file.path, arguments);
+ }
+ }
+ }
+
+ return unitsToAnalyze;
+}
+
+AnalyzeUnits ClangStaticAnalyzerRunControl::sortedUnitsToAnalyze()
+{
+ QTC_ASSERT(m_projectInfo.isValid(), return AnalyzeUnits());
+
+ AnalyzeUnits units;
+ const ProjectInfo::CompilerCallData compilerCallData = m_projectInfo.compilerCallData();
+ if (compilerCallData.isEmpty()) {
+ units = unitsToAnalyzeFromProjectParts(m_projectInfo.projectParts(),
+ m_wordWidth);
+ } else {
+ units = unitsToAnalyzeFromCompilerCallData(compilerCallData, m_wordWidth);
+ }
+
+ Utils::sort(units, [](const AnalyzeUnit &a1, const AnalyzeUnit &a2) -> bool {
+ return a1.file < a2.file;
+ });
+ return units;
+}
+
+static QDebug operator<<(QDebug debug, const Utils::Environment &environment)
+{
+ foreach (const QString &entry, environment.toStringList())
+ debug << "\n " << entry;
+ return debug;
+}
+
+static QDebug operator<<(QDebug debug, const AnalyzeUnits &analyzeUnits)
+{
+ foreach (const AnalyzeUnit &unit, analyzeUnits)
+ debug << "\n " << unit.file;
+ return debug;
+}
+
+static Core::Id toolchainType(ProjectExplorer::RunConfiguration *runConfiguration)
+{
+ QTC_ASSERT(runConfiguration, return Core::Id());
+ return ToolChainKitInformation::toolChain(runConfiguration->target()->kit())->typeId();
+}
+
+bool ClangStaticAnalyzerRunControl::startEngine()
+{
+ m_success = false;
+ emit starting(this);
+
+ QTC_ASSERT(m_projectInfo.isValid(), emit finished(); return false);
+ const Utils::FileName projectFile = m_projectInfo.project()->projectFilePath();
+ appendMessage(tr("Running Clang Static Analyzer on %1").arg(projectFile.toUserOutput())
+ + QLatin1Char('\n'), Utils::NormalMessageFormat);
+
+ // Check clang executable
+ bool isValidClangExecutable;
+ const QString executable = clangExecutableFromSettings(toolchainType(runConfiguration()),
+ &isValidClangExecutable);
+ if (!isValidClangExecutable) {
+ const QString errorMessage = tr("Clang Static Analyzer: Invalid executable \"%1\", stop.")
+ .arg(executable);
+ appendMessage(errorMessage + QLatin1Char('\n'), Utils::ErrorMessageFormat);
+ AnalyzerUtils::logToIssuesPane(Task::Error, errorMessage);
+ emit finished();
+ return false;
+ }
+ m_clangExecutable = executable;
+
+ // Create log dir
+ QTemporaryDir temporaryDir(QDir::tempPath() + QLatin1String("/qtc-clangstaticanalyzer-XXXXXX"));
+ temporaryDir.setAutoRemove(false);
+ if (!temporaryDir.isValid()) {
+ const QString errorMessage
+ = tr("Clang Static Analyzer: Failed to create temporary dir, stop.");
+ appendMessage(errorMessage + QLatin1Char('\n'), Utils::ErrorMessageFormat);
+ AnalyzerUtils::logToIssuesPane(Task::Error, errorMessage);
+ emit finished();
+ return false;
+ }
+ m_clangLogFileDir = temporaryDir.path();
+
+ // Collect files
+ const AnalyzeUnits unitsToProcess = sortedUnitsToAnalyze();
+ qCDebug(LOG) << "Files to process:" << unitsToProcess;
+ m_unitsToProcess = unitsToProcess;
+ m_initialFilesToProcessSize = m_unitsToProcess.count();
+ m_filesAnalyzed = 0;
+ m_filesNotAnalyzed = 0;
+
+ // Set up progress information
+ using namespace Core;
+ m_progress = QFutureInterface<void>();
+ FutureProgress *futureProgress
+ = ProgressManager::addTask(m_progress.future(), tr("Analyzing"), "ClangStaticAnalyzer");
+ futureProgress->setKeepOnFinish(FutureProgress::HideOnFinish);
+ connect(futureProgress, &FutureProgress::canceled,
+ this, &ClangStaticAnalyzerRunControl::onProgressCanceled);
+ m_progress.setProgressRange(0, m_initialFilesToProcessSize);
+ m_progress.reportStarted();
+
+ // Start process(es)
+ qCDebug(LOG) << "Environment:" << m_environment;
+ m_runners.clear();
+ const int parallelRuns = ClangStaticAnalyzerSettings::instance()->simultaneousProcesses();
+ QTC_ASSERT(parallelRuns >= 1, emit finished(); return false);
+ m_success = true;
+
+ if (m_unitsToProcess.isEmpty()) {
+ finalize();
+ return false;
+ }
+ while (m_runners.size() < parallelRuns && !m_unitsToProcess.isEmpty())
+ analyzeNextFile();
+ return true;
+}
+
+void ClangStaticAnalyzerRunControl::stopEngine()
+{
+ QSetIterator<ClangStaticAnalyzerRunner *> i(m_runners);
+ while (i.hasNext()) {
+ ClangStaticAnalyzerRunner *runner = i.next();
+ QObject::disconnect(runner, 0, this, 0);
+ delete runner;
+ }
+ m_runners.clear();
+ m_unitsToProcess.clear();
+ appendMessage(tr("Clang Static Analyzer stopped by user.") + QLatin1Char('\n'),
+ Utils::NormalMessageFormat);
+ m_progress.reportFinished();
+ emit finished();
+}
+
+void ClangStaticAnalyzerRunControl::analyzeNextFile()
+{
+ if (m_progress.isFinished())
+ return; // The previous call already reported that we are finished.
+
+ if (m_unitsToProcess.isEmpty()) {
+ if (m_runners.isEmpty())
+ finalize();
+ return;
+ }
+
+ const AnalyzeUnit unit = m_unitsToProcess.takeFirst();
+ qCDebug(LOG) << "analyzeNextFile:" << unit.file;
+
+ ClangStaticAnalyzerRunner *runner = createRunner();
+ m_runners.insert(runner);
+ QTC_ASSERT(runner->run(unit.file, unit.arguments), return);
+
+ appendMessage(tr("Analyzing \"%1\".").arg(
+ Utils::FileName::fromString(unit.file).toUserOutput()) + QLatin1Char('\n'),
+ Utils::StdOutFormat);
+}
+
+ClangStaticAnalyzerRunner *ClangStaticAnalyzerRunControl::createRunner()
+{
+ QTC_ASSERT(!m_clangExecutable.isEmpty(), return 0);
+ QTC_ASSERT(!m_clangLogFileDir.isEmpty(), return 0);
+
+ auto runner = new ClangStaticAnalyzerRunner(m_clangExecutable,
+ m_clangLogFileDir,
+ m_environment,
+ this);
+ connect(runner, &ClangStaticAnalyzerRunner::finishedWithSuccess,
+ this, &ClangStaticAnalyzerRunControl::onRunnerFinishedWithSuccess);
+ connect(runner, &ClangStaticAnalyzerRunner::finishedWithFailure,
+ this, &ClangStaticAnalyzerRunControl::onRunnerFinishedWithFailure);
+ return runner;
+}
+
+void ClangStaticAnalyzerRunControl::onRunnerFinishedWithSuccess(const QString &logFilePath)
+{
+ qCDebug(LOG) << "onRunnerFinishedWithSuccess:" << logFilePath;
+
+ QString errorMessage;
+ const QList<Diagnostic> diagnostics = LogFileReader::read(logFilePath, &errorMessage);
+ if (!errorMessage.isEmpty()) {
+ qCDebug(LOG) << "onRunnerFinishedWithSuccess: Error reading log file:" << errorMessage;
+ const QString filePath = qobject_cast<ClangStaticAnalyzerRunner *>(sender())->filePath();
+ appendMessage(tr("Failed to analyze \"%1\": %2").arg(filePath, errorMessage)
+ + QLatin1Char('\n')
+ , Utils::StdErrFormat);
+ } else {
+ ++m_filesAnalyzed;
+ if (!diagnostics.isEmpty())
+ emit newDiagnosticsAvailable(diagnostics);
+ }
+
+ handleFinished();
+}
+
+void ClangStaticAnalyzerRunControl::onRunnerFinishedWithFailure(const QString &errorMessage,
+ const QString &errorDetails)
+{
+ qCDebug(LOG) << "onRunnerFinishedWithFailure:" << errorMessage << errorDetails;
+
+ ++m_filesNotAnalyzed;
+ m_success = false;
+ const QString filePath = qobject_cast<ClangStaticAnalyzerRunner *>(sender())->filePath();
+ appendMessage(tr("Failed to analyze \"%1\": %2").arg(filePath, errorMessage)
+ + QLatin1Char('\n')
+ , Utils::StdErrFormat);
+ appendMessage(errorDetails, Utils::StdErrFormat);
+ AnalyzerUtils::logToIssuesPane(Task::Warning, errorMessage);
+ AnalyzerUtils::logToIssuesPane(Task::Warning, errorDetails);
+ handleFinished();
+}
+
+void ClangStaticAnalyzerRunControl::handleFinished()
+{
+ m_runners.remove(qobject_cast<ClangStaticAnalyzerRunner *>(sender()));
+ updateProgressValue();
+ sender()->deleteLater();
+ analyzeNextFile();
+}
+
+void ClangStaticAnalyzerRunControl::onProgressCanceled()
+{
+ Analyzer::AnalyzerManager::stopTool();
+ m_progress.reportCanceled();
+ m_progress.reportFinished();
+}
+
+void ClangStaticAnalyzerRunControl::updateProgressValue()
+{
+ m_progress.setProgressValue(m_initialFilesToProcessSize - m_unitsToProcess.size());
+}
+
+void ClangStaticAnalyzerRunControl::finalize()
+{
+ appendMessage(tr("Clang Static Analyzer finished: "
+ "Processed %1 files successfully, %2 failed.")
+ .arg(m_filesAnalyzed)
+ .arg(m_filesNotAnalyzed)
+ + QLatin1Char('\n'),
+ Utils::NormalMessageFormat);
+
+ if (m_filesAnalyzed == 0 && m_filesNotAnalyzed != 0) {
+ AnalyzerUtils::logToIssuesPane(Task::Error,
+ tr("Clang Static Analyzer: Failed to analyze any files."));
+ }
+
+ m_progress.reportFinished();
+ emit finished();
+}
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzer
diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.h b/src/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.h
new file mode 100644
index 0000000000..06cbc7ed93
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.h
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#ifndef CLANGSTATICANALYZERRUNCONTROL_H
+#define CLANGSTATICANALYZERRUNCONTROL_H
+
+#include <analyzerbase/analyzerruncontrol.h>
+#include <cpptools/projectinfo.h>
+#include <utils/environment.h>
+
+#include <QFutureInterface>
+#include <QStringList>
+
+namespace ClangStaticAnalyzer {
+namespace Internal {
+
+class ClangStaticAnalyzerRunner;
+class Diagnostic;
+
+struct AnalyzeUnit {
+ AnalyzeUnit(const QString &file, const QStringList &options)
+ : file(file), arguments(options) {}
+
+ QString file;
+ QStringList arguments; // without file itself and "-o somePath"
+};
+typedef QList<AnalyzeUnit> AnalyzeUnits;
+
+class ClangStaticAnalyzerRunControl : public Analyzer::AnalyzerRunControl
+{
+ Q_OBJECT
+
+public:
+ ClangStaticAnalyzerRunControl(ProjectExplorer::RunConfiguration *runConfiguration,
+ Core::Id runMode,
+ const CppTools::ProjectInfo &projectInfo);
+
+ bool startEngine();
+ void stopEngine();
+
+ bool success() const { return m_success; } // For testing.
+
+signals:
+ void newDiagnosticsAvailable(const QList<Diagnostic> &diagnostics);
+
+private:
+ AnalyzeUnits sortedUnitsToAnalyze();
+ void analyzeNextFile();
+ ClangStaticAnalyzerRunner *createRunner();
+
+ void onRunnerFinishedWithSuccess(const QString &logFilePath);
+ void onRunnerFinishedWithFailure(const QString &errorMessage, const QString &errorDetails);
+ void handleFinished();
+
+ void onProgressCanceled();
+ void updateProgressValue();
+
+ void finalize();
+
+private:
+ const CppTools::ProjectInfo m_projectInfo;
+ const unsigned char m_wordWidth;
+
+ Utils::Environment m_environment;
+ QString m_clangExecutable;
+ QString m_clangLogFileDir;
+ QFutureInterface<void> m_progress;
+ AnalyzeUnits m_unitsToProcess;
+ QSet<ClangStaticAnalyzerRunner *> m_runners;
+ int m_initialFilesToProcessSize;
+ int m_filesAnalyzed;
+ int m_filesNotAnalyzed;
+ bool m_success;
+};
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzer
+
+#endif // CLANGSTATICANALYZERRUNCONTROL_H
diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrolfactory.cpp b/src/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrolfactory.cpp
new file mode 100644
index 0000000000..b89ea1cee5
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrolfactory.cpp
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "clangstaticanalyzerruncontrolfactory.h"
+
+#include "clangstaticanalyzerconstants.h"
+
+#include <analyzerbase/analyzermanager.h>
+#include <analyzerbase/analyzerruncontrol.h>
+#include <analyzerbase/analyzerstartparameters.h>
+
+#include <coreplugin/icontext.h>
+
+#include <cpptools/cppmodelmanager.h>
+
+#include <projectexplorer/buildconfiguration.h>
+#include <projectexplorer/gcctoolchain.h>
+#include <projectexplorer/kit.h>
+#include <projectexplorer/kitinformation.h>
+#include <projectexplorer/project.h>
+#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/session.h>
+#include <projectexplorer/target.h>
+#include <projectexplorer/toolchain.h>
+
+#include <utils/qtcassert.h>
+
+using namespace Analyzer;
+using namespace ProjectExplorer;
+
+namespace ClangStaticAnalyzer {
+namespace Internal {
+
+ClangStaticAnalyzerRunControlFactory::ClangStaticAnalyzerRunControlFactory(
+ ClangStaticAnalyzerTool *tool,
+ QObject *parent)
+ : IRunControlFactory(parent)
+ , m_tool(tool)
+{
+ QTC_CHECK(m_tool);
+}
+
+bool ClangStaticAnalyzerRunControlFactory::canRun(RunConfiguration *runConfiguration,
+ Core::Id runMode) const
+{
+ if (runMode != Constants::CLANGSTATICANALYZER_RUN_MODE)
+ return false;
+
+ Project *project = runConfiguration->target()->project();
+ QTC_ASSERT(project, return false);
+ const Core::Context context = project->projectLanguages();
+ if (!context.contains(ProjectExplorer::Constants::LANG_CXX))
+ return false;
+
+ Target *target = runConfiguration->target();
+ QTC_ASSERT(target, return false);
+ Kit *kit = target->kit();
+ QTC_ASSERT(kit, return false);
+ ToolChain *toolChain = ToolChainKitInformation::toolChain(kit);
+ return toolChain && (toolChain->typeId() == ProjectExplorer::Constants::CLANG_TOOLCHAIN_TYPEID
+ || toolChain->typeId() == ProjectExplorer::Constants::GCC_TOOLCHAIN_TYPEID
+ || toolChain->typeId() == ProjectExplorer::Constants::MINGW_TOOLCHAIN_TYPEID
+ || toolChain->typeId() == ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID);
+}
+
+RunControl *ClangStaticAnalyzerRunControlFactory::create(RunConfiguration *runConfiguration,
+ Core::Id runMode,
+ QString *errorMessage)
+{
+ using namespace CppTools;
+ const ProjectInfo projectInfoBeforeBuild = m_tool->projectInfoBeforeBuild();
+ QTC_ASSERT(projectInfoBeforeBuild.isValid(), return 0);
+
+ QTC_ASSERT(runConfiguration, return 0);
+ Target * const target = runConfiguration->target();
+ QTC_ASSERT(target, return 0);
+ Project * const project = target->project();
+ QTC_ASSERT(project, return 0);
+
+ const ProjectInfo projectInfoAfterBuild = CppModelManager::instance()->projectInfo(project);
+
+ if (projectInfoAfterBuild.configurationOrFilesChanged(projectInfoBeforeBuild)) {
+ // If it's more than a release/debug build configuration change, e.g.
+ // a version control checkout, files might be not valid C++ anymore
+ // or even gone, so better stop here.
+
+ m_tool->resetCursorAndProjectInfoBeforeBuild();
+ if (errorMessage) {
+ *errorMessage = tr(
+ "The project configuration changed since the start of the Clang Static Analyzer. "
+ "Please re-run with current configuration.");
+ }
+ return 0;
+ }
+
+ return AnalyzerManager::createRunControl(runConfiguration, runMode);
+}
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzer
diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrolfactory.h b/src/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrolfactory.h
new file mode 100644
index 0000000000..6f9e5fc81c
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrolfactory.h
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#ifndef CLANGSTATICANALYZERRUNCONTROLFACTORY_H
+#define CLANGSTATICANALYZERRUNCONTROLFACTORY_H
+
+#include "clangstaticanalyzertool.h"
+
+#include <projectexplorer/runconfiguration.h>
+
+namespace ClangStaticAnalyzer {
+namespace Internal {
+
+class ClangStaticAnalyzerRunControlFactory : public ProjectExplorer::IRunControlFactory
+{
+ Q_OBJECT
+
+public:
+ explicit ClangStaticAnalyzerRunControlFactory(ClangStaticAnalyzerTool *tool,
+ QObject *parent = 0);
+
+ bool canRun(ProjectExplorer::RunConfiguration *runConfiguration,
+ Core::Id runMode) const;
+
+ ProjectExplorer::RunControl *create(ProjectExplorer::RunConfiguration *runConfiguration,
+ Core::Id runMode,
+ QString *errorMessage);
+
+private:
+ ClangStaticAnalyzerTool *m_tool;
+};
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzer
+
+#endif // CLANGSTATICANALYZERRUNCONTROLFACTORY_H
diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzerrunner.cpp b/src/plugins/clangstaticanalyzer/clangstaticanalyzerrunner.cpp
new file mode 100644
index 0000000000..00e0672d3b
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerrunner.cpp
@@ -0,0 +1,193 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "clangstaticanalyzerrunner.h"
+
+#include "clangstaticanalyzerconstants.h"
+
+#include <utils/synchronousprocess.h>
+
+#include <QDebug>
+#include <QDir>
+#include <QFileInfo>
+#include <QLoggingCategory>
+#include <QTemporaryFile>
+
+static Q_LOGGING_CATEGORY(LOG, "qtc.clangstaticanalyzer.runner")
+
+static QString generalProcessError()
+{
+ return QObject::tr("An error occurred with the clang static analyzer process.");
+}
+
+static QString finishedDueToCrash()
+{
+ return QObject::tr("Clang static analyzer crashed.");
+}
+
+static QStringList constructCommandLineArguments(const QString &filePath,
+ const QString &logFile,
+ const QStringList &options)
+{
+ QStringList arguments = QStringList()
+ << QLatin1String("--analyze")
+ << QLatin1String("-o")
+ << logFile
+ ;
+ arguments += options;
+ arguments << QDir::toNativeSeparators(filePath);
+ return arguments;
+}
+
+namespace ClangStaticAnalyzer {
+namespace Internal {
+
+QString finishedWithBadExitCode(int exitCode)
+{
+ return QObject::tr("Clang static analyzer finished with exit code: %1.").arg(exitCode);
+}
+
+ClangStaticAnalyzerRunner::ClangStaticAnalyzerRunner(const QString &clangExecutable,
+ const QString &clangLogFileDir,
+ const Utils::Environment &environment,
+ QObject *parent)
+ : QObject(parent)
+ , m_clangExecutable(clangExecutable)
+ , m_clangLogFileDir(clangLogFileDir)
+{
+ QTC_CHECK(!m_clangExecutable.isEmpty());
+ QTC_CHECK(!m_clangLogFileDir.isEmpty());
+
+ m_process.setProcessChannelMode(QProcess::MergedChannels);
+ m_process.setProcessEnvironment(environment.toProcessEnvironment());
+ m_process.setWorkingDirectory(m_clangLogFileDir); // Current clang-cl puts log file into working dir.
+ connect(&m_process, &QProcess::started,
+ this, &ClangStaticAnalyzerRunner::onProcessStarted);
+ connect(&m_process, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
+ this, &ClangStaticAnalyzerRunner::onProcessFinished);
+ connect(&m_process, static_cast<void (QProcess::*)(QProcess::ProcessError)>(&QProcess::error),
+ this, &ClangStaticAnalyzerRunner::onProcessError);
+ connect(&m_process, &QProcess::readyRead,
+ this, &ClangStaticAnalyzerRunner::onProcessOutput);
+}
+
+ClangStaticAnalyzerRunner::~ClangStaticAnalyzerRunner()
+{
+ Utils::SynchronousProcess::stopProcess(m_process);
+}
+
+bool ClangStaticAnalyzerRunner::run(const QString &filePath, const QStringList &compilerOptions)
+{
+ QTC_ASSERT(!m_clangExecutable.isEmpty(), return false);
+ QTC_CHECK(!compilerOptions.contains(QLatin1String("-o")));
+ QTC_CHECK(!compilerOptions.contains(filePath));
+
+ m_filePath = filePath;
+ m_processOutput.clear();
+
+ m_logFile = createLogFile(filePath);
+ QTC_ASSERT(!m_logFile.isEmpty(), return false);
+ const QStringList arguments = constructCommandLineArguments(filePath, m_logFile,
+ compilerOptions);
+ m_commandLine = (QStringList(m_clangExecutable) + arguments).join(QLatin1String("\" \""));
+
+ qCDebug(LOG) << "Starting" << m_commandLine;
+ m_process.start(m_clangExecutable, arguments);
+ return true;
+}
+
+QString ClangStaticAnalyzerRunner::filePath() const
+{
+ return m_filePath;
+}
+
+void ClangStaticAnalyzerRunner::onProcessStarted()
+{
+ emit started();
+}
+
+void ClangStaticAnalyzerRunner::onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus)
+{
+ if (exitStatus == QProcess::NormalExit) {
+ if (exitCode == 0)
+ emit finishedWithSuccess(actualLogFile());
+ else
+ emit finishedWithFailure(finishedWithBadExitCode(exitCode), processCommandlineAndOutput());
+ } else { // == QProcess::CrashExit
+ emit finishedWithFailure(finishedDueToCrash(), processCommandlineAndOutput());
+ }
+}
+
+void ClangStaticAnalyzerRunner::onProcessError(QProcess::ProcessError error)
+{
+ if (error == QProcess::Crashed)
+ return; // handled by slot of finished()
+
+ emit finishedWithFailure(generalProcessError(), processCommandlineAndOutput());
+}
+
+void ClangStaticAnalyzerRunner::onProcessOutput()
+{
+ m_processOutput.append(m_process.readAll());
+}
+
+QString ClangStaticAnalyzerRunner::createLogFile(const QString &filePath) const
+{
+ const QString fileName = QFileInfo(filePath).fileName();
+ const QString fileTemplate = m_clangLogFileDir
+ + QLatin1String("/report-") + fileName + QLatin1String("-XXXXXX.plist");
+
+ QTemporaryFile temporaryFile;
+ temporaryFile.setAutoRemove(false);
+ temporaryFile.setFileTemplate(fileTemplate);
+ if (temporaryFile.open()) {
+ temporaryFile.close();
+ return temporaryFile.fileName();
+ }
+ return QString();
+}
+
+QString ClangStaticAnalyzerRunner::processCommandlineAndOutput() const
+{
+ return QObject::tr("Command line: \"%1\"\n"
+ "Process Error: %2\n"
+ "Output:\n%3")
+ .arg(m_commandLine,
+ QString::number(m_process.error()),
+ QString::fromLocal8Bit(m_processOutput));
+}
+
+QString ClangStaticAnalyzerRunner::actualLogFile() const
+{
+ if (QFileInfo(m_logFile).size() == 0) {
+ // Current clang-cl ignores -o, always putting the log file into the working directory.
+ return m_clangLogFileDir + QLatin1Char('/') + QFileInfo(m_filePath).completeBaseName()
+ + QLatin1String(".plist");
+ }
+ return m_logFile;
+}
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzer
diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzerrunner.h b/src/plugins/clangstaticanalyzer/clangstaticanalyzerrunner.h
new file mode 100644
index 0000000000..92852d34c2
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerrunner.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#ifndef CLANGSTATICANALYZERRUNNER_H
+#define CLANGSTATICANALYZERRUNNER_H
+
+#include <QString>
+#include <QProcess>
+
+#include <utils/environment.h>
+#include <utils/qtcassert.h>
+
+namespace ClangStaticAnalyzer {
+namespace Internal {
+
+QString finishedWithBadExitCode(int exitCode); // exposed for tests
+
+class ClangStaticAnalyzerRunner : public QObject
+{
+ Q_OBJECT
+
+public:
+ ClangStaticAnalyzerRunner(const QString &clangExecutable,
+ const QString &clangLogFileDir,
+ const Utils::Environment &environment,
+ QObject *parent = 0);
+ ~ClangStaticAnalyzerRunner();
+
+ // compilerOptions is expected to contain everything except:
+ // (1) filePath, that is the file to analyze
+ // (2) -o output-file
+ bool run(const QString &filePath, const QStringList &compilerOptions = QStringList());
+
+ QString filePath() const;
+
+signals:
+ void started();
+ void finishedWithSuccess(const QString &logFilePath);
+ void finishedWithFailure(const QString &errorMessage, const QString &errorDetails);
+
+private:
+ void onProcessStarted();
+ void onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
+ void onProcessError(QProcess::ProcessError error);
+ void onProcessOutput();
+
+ QString createLogFile(const QString &filePath) const;
+ QString processCommandlineAndOutput() const;
+ QString actualLogFile() const;
+
+private:
+ QString m_clangExecutable;
+ QString m_clangLogFileDir;
+ QString m_filePath;
+ QString m_logFile;
+ QString m_commandLine;
+ QProcess m_process;
+ QByteArray m_processOutput;
+};
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzer
+
+#endif // CLANGSTATICANALYZERRUNNER_H
diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzersettings.cpp b/src/plugins/clangstaticanalyzer/clangstaticanalyzersettings.cpp
new file mode 100644
index 0000000000..803cbb02f0
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzersettings.cpp
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "clangstaticanalyzersettings.h"
+
+#include "clangstaticanalyzerconstants.h"
+
+#include <coreplugin/icore.h>
+
+#include <utils/hostosinfo.h>
+#include <utils/qtcassert.h>
+
+#include <QFileInfo>
+#include <QThread>
+
+static const char clangExecutableKey[] = "clangExecutable";
+static const char simultaneousProcessesKey[] = "simultaneousProcesses";
+
+namespace ClangStaticAnalyzer {
+namespace Internal {
+
+ClangStaticAnalyzerSettings::ClangStaticAnalyzerSettings()
+ : m_simultaneousProcesses(-1)
+{
+ readSettings();
+}
+
+ClangStaticAnalyzerSettings *ClangStaticAnalyzerSettings::instance()
+{
+ static ClangStaticAnalyzerSettings instance;
+ return &instance;
+}
+
+static QString clangExecutableFileName()
+{
+ return QLatin1String(Utils::HostOsInfo::isWindowsHost() ? "clang-cl.exe" : "clang");
+}
+
+QString ClangStaticAnalyzerSettings::defaultClangExecutable() const
+{
+ const QString shippedBinary = Core::ICore::libexecPath() + QLatin1Char('/')
+ + clangExecutableFileName();
+ if (QFileInfo(shippedBinary).isExecutable())
+ return shippedBinary;
+ return clangExecutableFileName();
+}
+
+QString ClangStaticAnalyzerSettings::clangExecutable(bool *isSet) const
+{
+ if (m_clangExecutable.isEmpty()) {
+ if (isSet)
+ *isSet = false;
+ return defaultClangExecutable();
+ }
+ if (isSet)
+ *isSet = true;
+ return m_clangExecutable;
+}
+
+void ClangStaticAnalyzerSettings::setClangExecutable(const QString &exectuable)
+{
+ m_clangExecutable = exectuable;
+}
+
+int ClangStaticAnalyzerSettings::simultaneousProcesses() const
+{
+ return m_simultaneousProcesses;
+}
+
+void ClangStaticAnalyzerSettings::setSimultaneousProcesses(int processes)
+{
+ QTC_ASSERT(processes >=1, return);
+ m_simultaneousProcesses = processes;
+}
+
+void ClangStaticAnalyzerSettings::readSettings()
+{
+ QSettings *settings = Core::ICore::settings();
+ settings->beginGroup(QLatin1String(Constants::SETTINGS_ID));
+
+ setClangExecutable(settings->value(QLatin1String(clangExecutableKey)).toString());
+
+ const int defaultSimultaneousProcesses = qMax(0, QThread::idealThreadCount() / 2);
+ setSimultaneousProcesses(settings->value(QLatin1String(simultaneousProcessesKey),
+ defaultSimultaneousProcesses).toInt());
+
+ settings->endGroup();
+}
+
+void ClangStaticAnalyzerSettings::writeSettings() const
+{
+ QSettings *settings = Core::ICore::settings();
+ settings->beginGroup(QLatin1String(Constants::SETTINGS_ID));
+ settings->setValue(QLatin1String(clangExecutableKey), m_clangExecutable);
+ settings->setValue(QLatin1String(simultaneousProcessesKey), simultaneousProcesses());
+ settings->endGroup();
+}
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzer
diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzersettings.h b/src/plugins/clangstaticanalyzer/clangstaticanalyzersettings.h
new file mode 100644
index 0000000000..5f7d80a01f
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzersettings.h
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#ifndef CLANGSTATICANALYZERSETTINGS_H
+#define CLANGSTATICANALYZERSETTINGS_H
+
+#include <QString>
+
+namespace ClangStaticAnalyzer {
+namespace Internal {
+
+class ClangStaticAnalyzerSettings
+{
+public:
+ static ClangStaticAnalyzerSettings *instance();
+
+ void writeSettings() const;
+
+ QString defaultClangExecutable() const;
+ QString clangExecutable(bool *isSet = nullptr) const;
+ void setClangExecutable(const QString &exectuable);
+
+ int simultaneousProcesses() const;
+ void setSimultaneousProcesses(int processes);
+
+private:
+ ClangStaticAnalyzerSettings();
+ void readSettings();
+
+ QString m_clangExecutable;
+ int m_simultaneousProcesses;
+};
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzer
+
+#endif // CLANGSTATICANALYZERSETTINGS_H
diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzertool.cpp b/src/plugins/clangstaticanalyzer/clangstaticanalyzertool.cpp
new file mode 100644
index 0000000000..9fe109d63d
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzertool.cpp
@@ -0,0 +1,332 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "clangstaticanalyzertool.h"
+
+#include "clangstaticanalyzerconstants.h"
+#include "clangstaticanalyzerdiagnostic.h"
+#include "clangstaticanalyzerdiagnosticmodel.h"
+#include "clangstaticanalyzerdiagnosticview.h"
+#include "clangstaticanalyzerruncontrol.h"
+
+#include <analyzerbase/analyzermanager.h>
+#include <coreplugin/coreconstants.h>
+#include <coreplugin/coreicons.h>
+#include <coreplugin/icore.h>
+#include <cpptools/cppmodelmanager.h>
+#include <projectexplorer/buildconfiguration.h>
+#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/session.h>
+#include <projectexplorer/target.h>
+
+#include <utils/checkablemessagebox.h>
+#include <utils/fancymainwindow.h>
+
+#include <QDockWidget>
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QListView>
+#include <QSortFilterProxyModel>
+#include <QToolButton>
+
+using namespace Analyzer;
+using namespace ProjectExplorer;
+
+namespace ClangStaticAnalyzer {
+namespace Internal {
+
+class DummyRunConfiguration : public RunConfiguration
+{
+ Q_OBJECT
+
+public:
+ DummyRunConfiguration(Target *parent)
+ : RunConfiguration(parent, "ClangStaticAnalyzer.DummyRunConfig")
+ {
+ setDefaultDisplayName(tr("Clang Static Analyzer"));
+ addExtraAspects();
+ }
+
+private:
+ QWidget *createConfigurationWidget() override { return 0; }
+};
+
+ClangStaticAnalyzerTool::ClangStaticAnalyzerTool(QObject *parent)
+ : QObject(parent)
+ , m_diagnosticModel(0)
+ , m_diagnosticFilterModel(0)
+ , m_diagnosticView(0)
+ , m_goBack(0)
+ , m_goNext(0)
+ , m_running(false)
+{
+ setObjectName(QLatin1String("ClangStaticAnalyzerTool"));
+}
+
+QWidget *ClangStaticAnalyzerTool::createWidgets()
+{
+ QTC_ASSERT(!m_diagnosticView, return 0);
+ QTC_ASSERT(!m_diagnosticModel, return 0);
+ QTC_ASSERT(!m_goBack, return 0);
+ QTC_ASSERT(!m_goNext, return 0);
+
+ //
+ // Diagnostic View
+ //
+ m_diagnosticView = new ClangStaticAnalyzerDiagnosticView;
+ m_diagnosticView->setFrameStyle(QFrame::NoFrame);
+ m_diagnosticView->setAttribute(Qt::WA_MacShowFocusRect, false);
+ m_diagnosticModel = new ClangStaticAnalyzerDiagnosticModel(this);
+ m_diagnosticFilterModel = new ClangStaticAnalyzerDiagnosticFilterModel(this);
+ 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"));
+ foreach (auto * const model,
+ QList<QAbstractItemModel *>() << m_diagnosticModel << m_diagnosticFilterModel) {
+ connect(model, &QAbstractItemModel::rowsInserted,
+ this, &ClangStaticAnalyzerTool::handleStateUpdate);
+ connect(model, &QAbstractItemModel::rowsRemoved,
+ this, &ClangStaticAnalyzerTool::handleStateUpdate);
+ connect(model, &QAbstractItemModel::modelReset,
+ this, &ClangStaticAnalyzerTool::handleStateUpdate);
+ connect(model, &QAbstractItemModel::layoutChanged, // For QSortFilterProxyModel::invalidate()
+ this, &ClangStaticAnalyzerTool::handleStateUpdate);
+ }
+
+ QDockWidget *issuesDock = AnalyzerManager::createDockWidget(ClangStaticAnalyzerToolId,
+ m_diagnosticView);
+ issuesDock->show();
+ Utils::FancyMainWindow *mw = AnalyzerManager::mainWindow();
+ mw->splitDockWidget(mw->toolBarDockWidget(), issuesDock, Qt::Vertical);
+
+ //
+ // Toolbar widget
+ //
+ QHBoxLayout *layout = new QHBoxLayout;
+ layout->setMargin(0);
+ layout->setSpacing(0);
+
+ QAction *action = 0;
+ QToolButton *button = 0;
+
+ // Go to previous diagnostic
+ action = new QAction(this);
+ action->setDisabled(true);
+ action->setIcon(Core::Icons::PREV.icon());
+ action->setToolTip(tr("Go to previous bug."));
+ connect(action, &QAction::triggered, m_diagnosticView, &DetailedErrorView::goBack);
+ button = new QToolButton;
+ button->setDefaultAction(action);
+ layout->addWidget(button);
+ m_goBack = action;
+
+ // Go to next diagnostic
+ action = new QAction(this);
+ action->setDisabled(true);
+ action->setIcon(Core::Icons::NEXT.icon());
+ action->setToolTip(tr("Go to next bug."));
+ connect(action, &QAction::triggered, m_diagnosticView, &DetailedErrorView::goNext);
+ button = new QToolButton;
+ button->setDefaultAction(action);
+ layout->addWidget(button);
+ m_goNext = action;
+
+ layout->addStretch();
+
+ QWidget *toolbarWidget = new QWidget;
+ toolbarWidget->setObjectName(QLatin1String("ClangStaticAnalyzerToolBarWidget"));
+ toolbarWidget->setLayout(layout);
+ return toolbarWidget;
+}
+
+AnalyzerRunControl *ClangStaticAnalyzerTool::createRunControl(RunConfiguration *runConfiguration,
+ Core::Id runMode)
+{
+ QTC_ASSERT(runConfiguration, return 0);
+ QTC_ASSERT(m_projectInfoBeforeBuild.isValid(), return 0);
+
+ // Some projects provides CompilerCallData once a build is finished,
+ // so pass on the updated Project Info unless no configuration change
+ // (defines/includes/files) happened.
+ Project *project = runConfiguration->target()->project();
+ QTC_ASSERT(project, return 0);
+ const CppTools::ProjectInfo projectInfoAfterBuild
+ = CppTools::CppModelManager::instance()->projectInfo(project);
+ QTC_ASSERT(!projectInfoAfterBuild.configurationOrFilesChanged(m_projectInfoBeforeBuild),
+ return 0);
+ m_projectInfoBeforeBuild = CppTools::ProjectInfo();
+
+ auto runControl = new ClangStaticAnalyzerRunControl(runConfiguration, runMode,
+ projectInfoAfterBuild);
+ connect(runControl, &ClangStaticAnalyzerRunControl::starting,
+ this, &ClangStaticAnalyzerTool::onEngineIsStarting);
+ connect(runControl, &ClangStaticAnalyzerRunControl::newDiagnosticsAvailable,
+ this, &ClangStaticAnalyzerTool::onNewDiagnosticsAvailable);
+ connect(runControl, &ClangStaticAnalyzerRunControl::finished,
+ this, &ClangStaticAnalyzerTool::onEngineFinished);
+ return runControl;
+}
+
+static bool dontStartAfterHintForDebugMode(Project *project)
+{
+ BuildConfiguration::BuildType buildType = BuildConfiguration::Unknown;
+ if (project) {
+ if (const Target *target = project->activeTarget()) {
+ if (const BuildConfiguration *buildConfig = target->activeBuildConfiguration())
+ buildType = buildConfig->buildType();
+ }
+ }
+
+ if (buildType == BuildConfiguration::Release) {
+ const QString wrongMode = ClangStaticAnalyzerTool::tr("Release");
+ const QString toolName = ClangStaticAnalyzerTool::tr("Clang Static Analyzer");
+ const QString title = ClangStaticAnalyzerTool::tr("Run %1 in %2 Mode?").arg(toolName)
+ .arg(wrongMode);
+ const QString message = ClangStaticAnalyzerTool::tr(
+ "<html><head/><body>"
+ "<p>You are trying to run the tool \"%1\" on an application in %2 mode. The tool is "
+ "designed to be used in Debug mode since enabled assertions can reduce the number of "
+ "false positives.</p>"
+ "<p>Do you want to continue and run the tool in %2 mode?</p>"
+ "</body></html>")
+ .arg(toolName).arg(wrongMode);
+ if (Utils::CheckableMessageBox::doNotAskAgainQuestion(Core::ICore::mainWindow(),
+ title, message, Core::ICore::settings(),
+ QLatin1String("ClangStaticAnalyzerCorrectModeWarning")) != QDialogButtonBox::Yes)
+ return true;
+ }
+
+ return false;
+}
+
+void ClangStaticAnalyzerTool::startTool()
+{
+ AnalyzerManager::showMode();
+
+ Project *project = SessionManager::startupProject();
+ QTC_ASSERT(project, emit finished(false); return);
+
+ if (dontStartAfterHintForDebugMode(project))
+ return;
+
+ m_diagnosticModel->clear();
+ setBusyCursor(true);
+ m_diagnosticFilterModel->setProject(project);
+ m_projectInfoBeforeBuild = CppTools::CppModelManager::instance()->projectInfo(project);
+ QTC_ASSERT(m_projectInfoBeforeBuild.isValid(), emit finished(false); return);
+ m_running = true;
+ handleStateUpdate();
+
+ Target * const target = project->activeTarget();
+ QTC_ASSERT(target, return);
+ DummyRunConfiguration *& rc = m_runConfigs[target];
+ if (!rc) {
+ rc = new DummyRunConfiguration(target);
+ connect(project, &Project::aboutToRemoveTarget, this,
+ [this](Target *t) { m_runConfigs.remove(t); });
+ const auto onProjectRemoved = [this](Project *p) {
+ foreach (Target * const t, p->targets())
+ m_runConfigs.remove(t);
+ };
+ connect(SessionManager::instance(), &SessionManager::aboutToRemoveProject, this,
+ onProjectRemoved, Qt::UniqueConnection);
+ }
+ ProjectExplorerPlugin::runRunConfiguration(rc, Constants::CLANGSTATICANALYZER_RUN_MODE);
+}
+
+CppTools::ProjectInfo ClangStaticAnalyzerTool::projectInfoBeforeBuild() const
+{
+ return m_projectInfoBeforeBuild;
+}
+
+void ClangStaticAnalyzerTool::resetCursorAndProjectInfoBeforeBuild()
+{
+ setBusyCursor(false);
+ m_projectInfoBeforeBuild = CppTools::ProjectInfo();
+}
+
+QList<Diagnostic> ClangStaticAnalyzerTool::diagnostics() const
+{
+ return m_diagnosticModel->diagnostics();
+}
+
+void ClangStaticAnalyzerTool::onEngineIsStarting()
+{
+ QTC_ASSERT(m_diagnosticModel, return);
+}
+
+void ClangStaticAnalyzerTool::onNewDiagnosticsAvailable(const QList<Diagnostic> &diagnostics)
+{
+ QTC_ASSERT(m_diagnosticModel, return);
+ m_diagnosticModel->addDiagnostics(diagnostics);
+}
+
+void ClangStaticAnalyzerTool::onEngineFinished()
+{
+ resetCursorAndProjectInfoBeforeBuild();
+ m_running = false;
+ handleStateUpdate();
+ emit finished(static_cast<ClangStaticAnalyzerRunControl *>(sender())->success());
+}
+
+void ClangStaticAnalyzerTool::setBusyCursor(bool busy)
+{
+ QTC_ASSERT(m_diagnosticView, return);
+ QCursor cursor(busy ? Qt::BusyCursor : Qt::ArrowCursor);
+ m_diagnosticView->setCursor(cursor);
+}
+
+void ClangStaticAnalyzerTool::handleStateUpdate()
+{
+ QTC_ASSERT(m_goBack, return);
+ QTC_ASSERT(m_goNext, return);
+ QTC_ASSERT(m_diagnosticModel, return);
+ QTC_ASSERT(m_diagnosticFilterModel, return);
+
+ const int issuesFound = m_diagnosticModel->diagnostics().count();
+ const int issuesVisible = m_diagnosticFilterModel->rowCount();
+ m_goBack->setEnabled(issuesVisible > 1);
+ m_goNext->setEnabled(issuesVisible > 1);
+
+ QString message = m_running ? tr("Clang Static Analyzer running.")
+ : tr("Clang Static Analyzer finished.");
+ message += QLatin1Char(' ');
+ if (issuesFound == 0) {
+ message += tr("No issues found.");
+ } else {
+ message += tr("%n issues found (%1 suppressed).", 0, issuesFound)
+ .arg(issuesFound - issuesVisible);
+ }
+ AnalyzerManager::showPermanentStatusMessage(ClangStaticAnalyzerToolId, message);
+}
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzer
+
+#include "clangstaticanalyzertool.moc"
diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzertool.h b/src/plugins/clangstaticanalyzer/clangstaticanalyzertool.h
new file mode 100644
index 0000000000..e5a748d0cd
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzertool.h
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#ifndef CLANGSTATICANALYZERTOOL_H
+#define CLANGSTATICANALYZERTOOL_H
+
+#include <analyzerbase/ianalyzertool.h>
+#include <cpptools/projectinfo.h>
+
+#include <QHash>
+
+namespace Analyzer { class DetailedErrorView; }
+namespace ProjectExplorer { class Target; }
+
+namespace ClangStaticAnalyzer {
+namespace Internal {
+
+class ClangStaticAnalyzerDiagnosticFilterModel;
+class ClangStaticAnalyzerDiagnosticModel;
+class ClangStaticAnalyzerDiagnosticView;
+class Diagnostic;
+class DummyRunConfiguration;
+
+const char ClangStaticAnalyzerToolId[] = "ClangStaticAnalyzer";
+
+class ClangStaticAnalyzerTool : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit ClangStaticAnalyzerTool(QObject *parent = 0);
+ CppTools::ProjectInfo projectInfoBeforeBuild() const;
+ void resetCursorAndProjectInfoBeforeBuild();
+
+ // For testing.
+ bool isRunning() const { return m_running; }
+ QList<Diagnostic> diagnostics() const;
+
+ QWidget *createWidgets();
+ Analyzer::AnalyzerRunControl *createRunControl(ProjectExplorer::RunConfiguration *runConfiguration,
+ Core::Id runMode);
+ void startTool();
+
+signals:
+ void finished(bool success); // For testing.
+
+private:
+ void onEngineIsStarting();
+ void onNewDiagnosticsAvailable(const QList<Diagnostic> &diagnostics);
+ void onEngineFinished();
+
+ void setBusyCursor(bool busy);
+ void handleStateUpdate();
+
+private:
+ CppTools::ProjectInfo m_projectInfoBeforeBuild;
+
+ ClangStaticAnalyzerDiagnosticModel *m_diagnosticModel;
+ ClangStaticAnalyzerDiagnosticFilterModel *m_diagnosticFilterModel;
+ ClangStaticAnalyzerDiagnosticView *m_diagnosticView;
+
+ QAction *m_goBack;
+ QAction *m_goNext;
+ QHash<ProjectExplorer::Target *, DummyRunConfiguration *> m_runConfigs;
+ bool m_running;
+};
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzer
+
+#endif // CLANGSTATICANALYZERTOOL_H
diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzerunittests.cpp b/src/plugins/clangstaticanalyzer/clangstaticanalyzerunittests.cpp
new file mode 100644
index 0000000000..12acb811a6
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerunittests.cpp
@@ -0,0 +1,117 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "clangstaticanalyzerunittests.h"
+
+#include "clangstaticanalyzerdiagnostic.h"
+#include "clangstaticanalyzertool.h"
+#include "clangstaticanalyzerutils.h"
+
+#include <analyzerbase/analyzermanager.h>
+#include <cpptools/cppmodelmanager.h>
+#include <cpptools/cpptoolstestcase.h>
+#include <projectexplorer/kitinformation.h>
+#include <projectexplorer/kitmanager.h>
+#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/toolchain.h>
+#include <utils/fileutils.h>
+
+#include <QEventLoop>
+#include <QSignalSpy>
+#include <QTemporaryDir>
+#include <QTimer>
+#include <QtTest>
+
+using namespace Analyzer;
+using namespace ProjectExplorer;
+using namespace Utils;
+
+namespace ClangStaticAnalyzer {
+namespace Internal {
+
+ClangStaticAnalyzerUnitTests::ClangStaticAnalyzerUnitTests(ClangStaticAnalyzerTool *analyzerTool,
+ QObject *parent)
+ : QObject(parent)
+ , m_analyzerTool(analyzerTool)
+ , m_tmpDir(0)
+{
+}
+
+void ClangStaticAnalyzerUnitTests::initTestCase()
+{
+ const QList<Kit *> allKits = KitManager::kits();
+ if (allKits.count() != 1)
+ QSKIP("This test requires exactly one kit to be present");
+ const ToolChain * const toolchain = ToolChainKitInformation::toolChain(allKits.first());
+ if (!toolchain)
+ QSKIP("This test requires that there is a kit with a toolchain.");
+ bool hasClangExecutable;
+ clangExecutableFromSettings(toolchain->typeId(), &hasClangExecutable);
+ if (!hasClangExecutable)
+ QSKIP("No clang suitable for analyzing found");
+
+ m_tmpDir = new CppTools::Tests::TemporaryCopiedDir(QLatin1String(":/unit-tests"));
+ QVERIFY(m_tmpDir->isValid());
+}
+
+void ClangStaticAnalyzerUnitTests::cleanupTestCase()
+{
+ delete m_tmpDir;
+}
+
+void ClangStaticAnalyzerUnitTests::testProject()
+{
+ QFETCH(QString, projectFilePath);
+ QFETCH(int, expectedDiagCount);
+
+ CppTools::Tests::ProjectOpenerAndCloser projectManager;
+ const CppTools::ProjectInfo projectInfo = projectManager.open(projectFilePath, true);
+ QVERIFY(projectInfo.isValid());
+ AnalyzerManager::selectAction(ClangStaticAnalyzerToolId, /* alsoRunIt = */ true);
+ QSignalSpy waiter(m_analyzerTool, SIGNAL(finished(bool)));
+ QVERIFY(waiter.wait(30000));
+ const QList<QVariant> arguments = waiter.takeFirst();
+ QVERIFY(arguments.first().toBool());
+ QCOMPARE(m_analyzerTool->diagnostics().count(), expectedDiagCount);
+}
+
+void ClangStaticAnalyzerUnitTests::testProject_data()
+{
+ QTest::addColumn<QString>("projectFilePath");
+ QTest::addColumn<int>("expectedDiagCount");
+
+ QTest::newRow("simple qbs project")
+ << QString(m_tmpDir->absolutePath("simple/simple.qbs")) << 1;
+ QTest::newRow("simple qmake project")
+ << QString(m_tmpDir->absolutePath("simple/simple.pro")) << 1;
+
+ QTest::newRow("qt-widgets-app qbs project")
+ << QString(m_tmpDir->absolutePath("qt-widgets-app/qt-widgets-app.qbs")) << 0;
+ QTest::newRow("qt-widgets-app qmake project")
+ << QString(m_tmpDir->absolutePath("qt-widgets-app/qt-widgets-app.pro")) << 0;
+}
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzerPlugin
diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzerunittests.h b/src/plugins/clangstaticanalyzer/clangstaticanalyzerunittests.h
new file mode 100644
index 0000000000..b49ef5b1d3
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerunittests.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#ifndef CLANGSTATICANALYZERUNITTESTS_H
+#define CLANGSTATICANALYZERUNITTESTS_H
+
+#include <QObject>
+#include <QTemporaryDir>
+
+namespace CppTools { namespace Tests { class TemporaryCopiedDir; } }
+
+namespace ClangStaticAnalyzer {
+namespace Internal {
+class ClangStaticAnalyzerTool;
+
+class ClangStaticAnalyzerUnitTests : public QObject
+{
+ Q_OBJECT
+
+public:
+ ClangStaticAnalyzerUnitTests(ClangStaticAnalyzerTool *analyzerTool, QObject *parent = 0);
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+ void testProject();
+ void testProject_data();
+
+private:
+ ClangStaticAnalyzerTool * const m_analyzerTool;
+ CppTools::Tests::TemporaryCopiedDir *m_tmpDir;
+};
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzerPlugin
+
+#endif // Include guard
+
diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzerunittests.qrc b/src/plugins/clangstaticanalyzer/clangstaticanalyzerunittests.qrc
new file mode 100644
index 0000000000..d8a1a8674e
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerunittests.qrc
@@ -0,0 +1,13 @@
+<RCC>
+ <qresource prefix="/">
+ <file>unit-tests/simple/main.cpp</file>
+ <file>unit-tests/simple/simple.qbs</file>
+ <file>unit-tests/simple/simple.pro</file>
+ <file>unit-tests/qt-widgets-app/main.cpp</file>
+ <file>unit-tests/qt-widgets-app/mainwindow.cpp</file>
+ <file>unit-tests/qt-widgets-app/mainwindow.h</file>
+ <file>unit-tests/qt-widgets-app/mainwindow.ui</file>
+ <file>unit-tests/qt-widgets-app/qt-widgets-app.pro</file>
+ <file>unit-tests/qt-widgets-app/qt-widgets-app.qbs</file>
+ </qresource>
+</RCC>
diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzerutils.cpp b/src/plugins/clangstaticanalyzer/clangstaticanalyzerutils.cpp
new file mode 100644
index 0000000000..1c7af5176b
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerutils.cpp
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "clangstaticanalyzerutils.h"
+
+#include "clangstaticanalyzerdiagnostic.h"
+#include "clangstaticanalyzersettings.h"
+
+#include <projectexplorer/projectexplorerconstants.h>
+
+#include <utils/environment.h>
+
+#include <QCoreApplication>
+#include <QFileInfo>
+
+static bool isFileExecutable(const QString &executablePath)
+{
+ if (executablePath.isEmpty())
+ return false;
+
+ const QFileInfo fileInfo(executablePath);
+ return fileInfo.isFile() && fileInfo.isExecutable();
+}
+
+namespace ClangStaticAnalyzer {
+namespace Internal {
+
+QString clangExecutableFromSettings(Core::Id toolchainType, bool *isValid)
+{
+ QString exeFromSettings = ClangStaticAnalyzerSettings::instance()->clangExecutable();
+ if (toolchainType == ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID)
+ exeFromSettings.replace(QLatin1String("clang.exe"), QLatin1String("clang-cl.exe"));
+ return clangExecutable(exeFromSettings, isValid);
+}
+
+QString clangExecutable(const QString &fileNameOrPath, bool *isValid)
+{
+ QString executable = fileNameOrPath;
+ if (executable.isEmpty()) {
+ *isValid = false;
+ return executable;
+ }
+
+ if (!QFileInfo(executable).isAbsolute()) {
+ const Utils::Environment &environment = Utils::Environment::systemEnvironment();
+ const QString executableFromPath = environment.searchInPath(executable).toString();
+ if (executableFromPath.isEmpty()) {
+ *isValid = false;
+ return executable;
+ }
+ executable = executableFromPath;
+ }
+
+ *isValid = isFileExecutable(executable) && isClangExecutableUsable(executable);
+ return executable;
+}
+
+QString createFullLocationString(const Analyzer::DiagnosticLocation &location)
+{
+ const QString filePath = location.filePath;
+ const QString lineNumber = QString::number(location.line);
+ 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/src/plugins/clangstaticanalyzer/clangstaticanalyzerutils.h b/src/plugins/clangstaticanalyzer/clangstaticanalyzerutils.h
new file mode 100644
index 0000000000..63f6f6517f
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerutils.h
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#ifndef CLANGSTATICANALYZERUTILS_H
+#define CLANGSTATICANALYZERUTILS_H
+
+#include <coreplugin/id.h>
+
+#include <QtGlobal>
+
+QT_BEGIN_NAMESPACE
+class QString;
+QT_END_NAMESPACE
+
+namespace Analyzer { class DiagnosticLocation; }
+
+namespace ClangStaticAnalyzer {
+namespace Internal {
+
+bool isClangExecutableUsable(const QString &filePath, QString *errorMessage = 0);
+
+QString clangExecutable(const QString &fileNameOrPath, bool *isValid);
+QString clangExecutableFromSettings(Core::Id toolchainType, bool *isValid);
+
+QString createFullLocationString(const Analyzer::DiagnosticLocation &location);
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzer
+
+#endif // CLANGSTATICANALYZERUTILS_H
diff --git a/src/plugins/clangstaticanalyzer/tests/clangstaticanalyzerautotest.qbs b/src/plugins/clangstaticanalyzer/tests/clangstaticanalyzerautotest.qbs
new file mode 100644
index 0000000000..88dfcb32a1
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/tests/clangstaticanalyzerautotest.qbs
@@ -0,0 +1,11 @@
+import qbs
+
+QtcAutotest {
+ Depends { name: "Qt.widgets" }
+ Depends { name: "AnalyzerBase" }
+ Depends { name: "Utils" }
+
+ property path pluginDir: "../../"
+ cpp.defines: base.concat('SRCDIR="' + sourceDirectory + '"')
+ cpp.includePaths: base.concat(pluginDir + "/..")
+}
diff --git a/src/plugins/clangstaticanalyzer/tests/clangstaticanalyzerlogfilereader/clangstaticanalyzerlogfilereader.pro b/src/plugins/clangstaticanalyzer/tests/clangstaticanalyzerlogfilereader/clangstaticanalyzerlogfilereader.pro
new file mode 100644
index 0000000000..d9dda64f85
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/tests/clangstaticanalyzerlogfilereader/clangstaticanalyzerlogfilereader.pro
@@ -0,0 +1,14 @@
+include(../tests.pri)
+
+TARGET = tst_clangstaticanalyzerlogfilereader
+
+DEFINES += SRCDIR=\\\"$$PWD/\\\"
+
+SOURCES += \
+ tst_clangstaticanalyzerlogfilereader.cpp \
+ $$PLUGINDIR/clangstaticanalyzerdiagnostic.cpp \
+ $$PLUGINDIR/clangstaticanalyzerlogfilereader.cpp
+
+HEADERS += \
+ $$PLUGINDIR/clangstaticanalyzerdiagnostic.h \
+ $$PLUGINDIR/clangstaticanalyzerlogfilereader.h
diff --git a/src/plugins/clangstaticanalyzer/tests/clangstaticanalyzerlogfilereader/clangstaticanalyzerlogfilereader.qbs b/src/plugins/clangstaticanalyzer/tests/clangstaticanalyzerlogfilereader/clangstaticanalyzerlogfilereader.qbs
new file mode 100644
index 0000000000..a1ac819f5f
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/tests/clangstaticanalyzerlogfilereader/clangstaticanalyzerlogfilereader.qbs
@@ -0,0 +1,21 @@
+import qbs
+import "../clangstaticanalyzerautotest.qbs" as ClangStaticAnalyzerAutotest
+
+ClangStaticAnalyzerAutotest {
+ name: "ClangStaticAnalyzerLogFileReader Autotest"
+
+ Group {
+ name: "sources from plugin"
+ prefix: pluginDir + '/'
+ files: [
+ "clangstaticanalyzerdiagnostic.cpp",
+ "clangstaticanalyzerdiagnostic.h",
+ "clangstaticanalyzerlogfilereader.cpp",
+ "clangstaticanalyzerlogfilereader.h",
+ ]
+ }
+
+ files: [
+ "tst_clangstaticanalyzerlogfilereader.cpp"
+ ]
+}
diff --git a/src/plugins/clangstaticanalyzer/tests/clangstaticanalyzerlogfilereader/data/noDiagnostics.plist b/src/plugins/clangstaticanalyzer/tests/clangstaticanalyzerlogfilereader/data/noDiagnostics.plist
new file mode 100644
index 0000000000..2cccbf770f
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/tests/clangstaticanalyzerlogfilereader/data/noDiagnostics.plist
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>clang_version</key>
+<string>Ubuntu clang version 3.5-1ubuntu1 (trunk) (based on LLVM 3.5)</string>
+ <key>files</key>
+ <array>
+ </array>
+ <key>diagnostics</key>
+ <array>
+ </array>
+</dict>
+</plist> \ No newline at end of file
diff --git a/src/plugins/clangstaticanalyzer/tests/clangstaticanalyzerlogfilereader/data/someDiagnostics.plist b/src/plugins/clangstaticanalyzer/tests/clangstaticanalyzerlogfilereader/data/someDiagnostics.plist
new file mode 100644
index 0000000000..3fa5effad2
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/tests/clangstaticanalyzerlogfilereader/data/someDiagnostics.plist
@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>clang_version</key>
+<string>Ubuntu clang version 3.5-1ubuntu1 (trunk) (based on LLVM 3.5)</string>
+ <key>files</key>
+ <array>
+ <string>../csatestproject/core.CallAndMessage3.cpp</string>
+ </array>
+ <key>diagnostics</key>
+ <array>
+ <dict>
+ <key>path</key>
+ <array>
+ <dict>
+ <key>kind</key><string>control</string>
+ <key>edges</key>
+ <array>
+ <dict>
+ <key>start</key>
+ <array>
+ <dict>
+ <key>line</key><integer>34</integer>
+ <key>col</key><integer>3</integer>
+ <key>file</key><integer>0</integer>
+ </dict>
+ <dict>
+ <key>line</key><integer>34</integer>
+ <key>col</key><integer>6</integer>
+ <key>file</key><integer>0</integer>
+ </dict>
+ </array>
+ <key>end</key>
+ <array>
+ <dict>
+ <key>line</key><integer>35</integer>
+ <key>col</key><integer>3</integer>
+ <key>file</key><integer>0</integer>
+ </dict>
+ <dict>
+ <key>line</key><integer>35</integer>
+ <key>col</key><integer>5</integer>
+ <key>file</key><integer>0</integer>
+ </dict>
+ </array>
+ </dict>
+ </array>
+ </dict>
+ <dict>
+ <key>kind</key><string>event</string>
+ <key>location</key>
+ <dict>
+ <key>line</key><integer>35</integer>
+ <key>col</key><integer>3</integer>
+ <key>file</key><integer>0</integer>
+ </dict>
+ <key>ranges</key>
+ <array>
+ <array>
+ <dict>
+ <key>line</key><integer>35</integer>
+ <key>col</key><integer>3</integer>
+ <key>file</key><integer>0</integer>
+ </dict>
+ <dict>
+ <key>line</key><integer>35</integer>
+ <key>col</key><integer>9</integer>
+ <key>file</key><integer>0</integer>
+ </dict>
+ </array>
+ </array>
+ <key>depth</key><integer>0</integer>
+ <key>extended_message</key>
+ <string>Null pointer value stored to &apos;foo&apos;</string>
+ <key>message</key>
+ <string>Null pointer value stored to &apos;foo&apos;</string>
+ </dict>
+ <dict>
+ <key>kind</key><string>control</string>
+ <key>edges</key>
+ <array>
+ <dict>
+ <key>start</key>
+ <array>
+ <dict>
+ <key>line</key><integer>35</integer>
+ <key>col</key><integer>3</integer>
+ <key>file</key><integer>0</integer>
+ </dict>
+ <dict>
+ <key>line</key><integer>35</integer>
+ <key>col</key><integer>5</integer>
+ <key>file</key><integer>0</integer>
+ </dict>
+ </array>
+ <key>end</key>
+ <array>
+ <dict>
+ <key>line</key><integer>36</integer>
+ <key>col</key><integer>3</integer>
+ <key>file</key><integer>0</integer>
+ </dict>
+ <dict>
+ <key>line</key><integer>36</integer>
+ <key>col</key><integer>5</integer>
+ <key>file</key><integer>0</integer>
+ </dict>
+ </array>
+ </dict>
+ </array>
+ </dict>
+ <dict>
+ <key>kind</key><string>event</string>
+ <key>location</key>
+ <dict>
+ <key>line</key><integer>36</integer>
+ <key>col</key><integer>3</integer>
+ <key>file</key><integer>0</integer>
+ </dict>
+ <key>ranges</key>
+ <array>
+ <array>
+ <dict>
+ <key>line</key><integer>36</integer>
+ <key>col</key><integer>3</integer>
+ <key>file</key><integer>0</integer>
+ </dict>
+ <dict>
+ <key>line</key><integer>36</integer>
+ <key>col</key><integer>5</integer>
+ <key>file</key><integer>0</integer>
+ </dict>
+ </array>
+ </array>
+ <key>depth</key><integer>0</integer>
+ <key>extended_message</key>
+ <string>Called function pointer is null (null dereference)</string>
+ <key>message</key>
+ <string>Called function pointer is null (null dereference)</string>
+ </dict>
+ </array>
+ <key>description</key><string>Called function pointer is null (null dereference)</string>
+ <key>category</key><string>Logic error</string>
+ <key>type</key><string>Called function pointer is null (null dereference)</string>
+ <key>issue_context_kind</key><string>function</string>
+ <key>issue_context</key><string>test</string>
+ <key>issue_hash</key><string>3</string>
+ <key>location</key>
+ <dict>
+ <key>line</key><integer>36</integer>
+ <key>col</key><integer>3</integer>
+ <key>file</key><integer>0</integer>
+ </dict>
+ <key>HTMLDiagnostics_files</key>
+ <array>
+ <string>report-6c3c2a.html</string>
+ </array>
+ </dict>
+ </array>
+</dict>
+</plist> \ No newline at end of file
diff --git a/src/plugins/clangstaticanalyzer/tests/clangstaticanalyzerlogfilereader/tst_clangstaticanalyzerlogfilereader.cpp b/src/plugins/clangstaticanalyzer/tests/clangstaticanalyzerlogfilereader/tst_clangstaticanalyzerlogfilereader.cpp
new file mode 100644
index 0000000000..e8050c514e
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/tests/clangstaticanalyzerlogfilereader/tst_clangstaticanalyzerlogfilereader.cpp
@@ -0,0 +1,162 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include <clangstaticanalyzer/clangstaticanalyzerlogfilereader.h>
+
+#include <utils/fileutils.h>
+
+#include <QtTest>
+
+enum { debug = 0 };
+
+using namespace Analyzer;
+using namespace ClangStaticAnalyzer::Internal;
+
+namespace {
+
+QDebug operator<<(QDebug dbg, const ExplainingStep &step)
+{
+ dbg << '\n'
+ << " ExplainingStep\n"
+ << " location:" << step.location << '\n'
+ << " ranges:\n";
+ foreach (const DiagnosticLocation &location, step.ranges)
+ dbg << " " << location << '\n';
+ dbg
+ << " message:" << step.message << '\n'
+ << " extendedMessage:" << step.extendedMessage << '\n'
+ << " depth:" << step.depth << '\n';
+ return dbg;
+}
+
+QDebug operator<<(QDebug dbg, const Diagnostic &diagnostic)
+{
+ dbg << "\nDiagnostic\n"
+ << " description:" << diagnostic.description << '\n'
+ << " category:" << diagnostic.category << '\n'
+ << " type:" << diagnostic.type << '\n'
+ << " issueContextKind:" << diagnostic.issueContextKind << '\n'
+ << " issueContext:" << diagnostic.issueContext << '\n'
+ << " location:" << diagnostic.location << '\n'
+ << " explaining steps:\n";
+ foreach (const ExplainingStep &explaingStep, diagnostic.explainingSteps)
+ dbg << explaingStep;
+ return dbg;
+}
+
+bool createEmptyFile(const QString &filePath)
+{
+ Utils::FileSaver saver(filePath);
+ return saver.write("") && saver.finalize();
+}
+
+QString testFilePath(const QString &relativePath)
+{
+ const QString fullPath = QString::fromLatin1(SRCDIR) + relativePath;
+ const QFileInfo fi(fullPath);
+ if (fi.exists() && fi.isReadable())
+ return fullPath;
+ return QString();
+}
+
+} // anonymous namespace
+
+class ClangStaticAnalyzerLogFileReaderTest : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void readEmptyFile();
+ void readFileWithNoDiagnostics();
+ void readFileWithDiagnostics();
+};
+
+void ClangStaticAnalyzerLogFileReaderTest::readEmptyFile()
+{
+ const QString filePath = QDir::tempPath() + QLatin1String("/empty.file");
+ QVERIFY(createEmptyFile(filePath));
+
+ QString errorMessage;
+ const QList<Diagnostic> diagnostics = LogFileReader::read(filePath, &errorMessage);
+ QVERIFY(!errorMessage.isEmpty());
+ if (debug)
+ qDebug() << errorMessage;
+ QVERIFY(diagnostics.isEmpty());
+}
+
+void ClangStaticAnalyzerLogFileReaderTest::readFileWithNoDiagnostics()
+{
+ const QString filePath = testFilePath(QLatin1String("/data/noDiagnostics.plist"));
+
+ QString errorMessage;
+ const QList<Diagnostic> diagnostics = LogFileReader::read(filePath, &errorMessage);
+ QVERIFY(errorMessage.isEmpty());
+ QVERIFY(diagnostics.isEmpty());
+}
+
+void ClangStaticAnalyzerLogFileReaderTest::readFileWithDiagnostics()
+{
+ const QString filePath = testFilePath(QLatin1String("/data/someDiagnostics.plist"));
+
+ QString errorMessage;
+ const QList<Diagnostic> diagnostics = LogFileReader::read(filePath, &errorMessage);
+ QVERIFY(errorMessage.isEmpty());
+ QVERIFY(!diagnostics.isEmpty());
+
+ const QString commonPath = QLatin1String("../csatestproject/core.CallAndMessage3.cpp");
+
+ const Diagnostic d1 = diagnostics.first();
+ if (debug)
+ qDebug() << d1;
+ QCOMPARE(d1.description, QLatin1String("Called function pointer is null (null dereference)"));
+ QCOMPARE(d1.category, QLatin1String("Logic error"));
+ QCOMPARE(d1.type, d1.description);
+ QCOMPARE(d1.issueContextKind, QLatin1String("function"));
+ QCOMPARE(d1.issueContext, QLatin1String("test"));
+ QCOMPARE(d1.location, DiagnosticLocation(commonPath, 36, 3));
+
+ QCOMPARE(d1.explainingSteps.size(), 2);
+ const ExplainingStep step1 = d1.explainingSteps.at(0);
+ QCOMPARE(step1.location, DiagnosticLocation(commonPath, 35, 3));
+ QCOMPARE(step1.ranges.size(), 2);
+ QCOMPARE(step1.ranges.at(0), DiagnosticLocation(commonPath, 35, 3));
+ QCOMPARE(step1.ranges.at(1), DiagnosticLocation(commonPath, 35, 9));
+ QCOMPARE(step1.depth, 0);
+ QCOMPARE(step1.message, QLatin1String("Null pointer value stored to 'foo'"));
+ QCOMPARE(step1.extendedMessage, step1.message);
+
+ const ExplainingStep step2 = d1.explainingSteps.at(1);
+ QCOMPARE(step2.location, DiagnosticLocation(commonPath, 36, 3));
+ QCOMPARE(step2.ranges.size(), 2);
+ QCOMPARE(step2.ranges.at(0), DiagnosticLocation(commonPath, 36, 3));
+ QCOMPARE(step2.ranges.at(1), DiagnosticLocation(commonPath, 36, 5));
+ QCOMPARE(step2.depth, 0);
+ QCOMPARE(step2.message, QLatin1String("Called function pointer is null (null dereference)"));
+ QCOMPARE(step2.extendedMessage, step2.message);
+}
+
+QTEST_MAIN(ClangStaticAnalyzerLogFileReaderTest)
+
+#include "tst_clangstaticanalyzerlogfilereader.moc"
diff --git a/src/plugins/clangstaticanalyzer/tests/clangstaticanalyzerrunner/clangstaticanalyzerrunner.pro b/src/plugins/clangstaticanalyzer/tests/clangstaticanalyzerrunner/clangstaticanalyzerrunner.pro
new file mode 100644
index 0000000000..b19bae4092
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/tests/clangstaticanalyzerrunner/clangstaticanalyzerrunner.pro
@@ -0,0 +1,11 @@
+include(../tests.pri)
+
+TARGET = tst_clangstaticanalyzerrunnertest
+
+DEFINES += SRCDIR=\\\"$$PWD/\\\"
+
+SOURCES += \
+ tst_clangstaticanalyzerrunner.cpp \
+ $$PLUGINDIR/clangstaticanalyzerrunner.cpp
+HEADERS += \
+ $$PLUGINDIR/clangstaticanalyzerrunner.h
diff --git a/src/plugins/clangstaticanalyzer/tests/clangstaticanalyzerrunner/clangstaticanalyzerrunner.qbs b/src/plugins/clangstaticanalyzer/tests/clangstaticanalyzerrunner/clangstaticanalyzerrunner.qbs
new file mode 100644
index 0000000000..6b0f9da3f3
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/tests/clangstaticanalyzerrunner/clangstaticanalyzerrunner.qbs
@@ -0,0 +1,19 @@
+import qbs
+import "../clangstaticanalyzerautotest.qbs" as ClangStaticAnalyzerAutotest
+
+ClangStaticAnalyzerAutotest {
+ name: "ClangStaticAnalyzerRunner Autotest"
+
+ Group {
+ name: "sources from plugin"
+ prefix: pluginDir + '/'
+ files: [
+ "clangstaticanalyzerrunner.cpp",
+ "clangstaticanalyzerrunner.h",
+ ]
+ }
+
+ files: [
+ "tst_clangstaticanalyzerrunner.cpp",
+ ]
+}
diff --git a/src/plugins/clangstaticanalyzer/tests/clangstaticanalyzerrunner/tst_clangstaticanalyzerrunner.cpp b/src/plugins/clangstaticanalyzer/tests/clangstaticanalyzerrunner/tst_clangstaticanalyzerrunner.cpp
new file mode 100644
index 0000000000..712875c329
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/tests/clangstaticanalyzerrunner/tst_clangstaticanalyzerrunner.cpp
@@ -0,0 +1,176 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include <clangstaticanalyzer/clangstaticanalyzerconstants.h>
+#include <clangstaticanalyzer/clangstaticanalyzerrunner.h>
+
+#include <QtTest>
+
+using namespace ClangStaticAnalyzer::Internal;
+
+class ClangStaticAnalyzerRunnerTest : public QObject
+{
+ Q_OBJECT
+
+public:
+ ClangStaticAnalyzerRunnerTest() {}
+ virtual ~ClangStaticAnalyzerRunnerTest() {}
+
+private slots:
+ void runWithTestCodeGeneratedOneIssue();
+ void runWithNonExistentFileToAnalyze();
+};
+
+static bool writeFile(const QString &filePath, const QByteArray &source)
+{
+ Utils::FileSaver saver(filePath);
+ return saver.write(source) && saver.finalize();
+}
+
+class ClangStaticAnalyzerRunnerSignalTester
+{
+public:
+ ClangStaticAnalyzerRunnerSignalTester(ClangStaticAnalyzerRunner *runner);
+
+ bool expectStartedSignal();
+ bool expectFinishWithSuccessSignal();
+ bool expectFinishWithFailureSignal(const QString &expectedErrorMessage = QString());
+
+private:
+ QSignalSpy m_spyStarted;
+ QSignalSpy m_spyFinishedWithFailure;
+ QSignalSpy m_spyFinishedWithSuccess;
+};
+
+ClangStaticAnalyzerRunnerSignalTester::ClangStaticAnalyzerRunnerSignalTester(
+ ClangStaticAnalyzerRunner *runner)
+ : m_spyStarted(runner, SIGNAL(started()))
+ , m_spyFinishedWithFailure(runner, SIGNAL(finishedWithFailure(QString,QString)))
+ , m_spyFinishedWithSuccess(runner, SIGNAL(finishedWithSuccess(QString)))
+{
+}
+
+bool ClangStaticAnalyzerRunnerSignalTester::expectStartedSignal()
+{
+ if (m_spyStarted.wait()) {
+ if (m_spyStarted.size() != 1) {
+ qDebug() << "started() emitted more than once.";
+ return false;
+ }
+ } else {
+ qDebug() << "started() not emitted.";
+ return false;
+ }
+
+ return true;
+}
+
+bool ClangStaticAnalyzerRunnerSignalTester::expectFinishWithSuccessSignal()
+{
+ if (m_spyFinishedWithSuccess.wait()) {
+ if (m_spyFinishedWithSuccess.size() != 1) {
+ qDebug() << "finishedWithSuccess() emitted more than once.";
+ return false;
+ }
+ } else {
+ qDebug() << "finishedWithSuccess() not emitted.";
+ return false;
+ }
+
+ if (m_spyFinishedWithFailure.size() != 0) {
+ qDebug() << "finishedWithFailure() emitted.";
+ return false;
+ }
+
+ return true;
+}
+
+bool ClangStaticAnalyzerRunnerSignalTester::expectFinishWithFailureSignal(
+ const QString &expectedErrorMessage)
+{
+ if (m_spyFinishedWithFailure.wait()) {
+ if (m_spyFinishedWithFailure.size() == 1) {
+ const QList<QVariant> args = m_spyFinishedWithFailure.at(0);
+ const QString errorMessage = args.at(0).toString();
+ if (errorMessage == expectedErrorMessage) {
+ if (m_spyFinishedWithSuccess.size() != 0) {
+ qDebug() << "Got expected error, but finishedWithSuccess() was also emitted.";
+ return false;
+ }
+ return true;
+ } else {
+ qDebug() << "Actual error message:" << errorMessage;
+ qDebug() << "Expected error message:" << expectedErrorMessage;
+ return false;
+ }
+ } else {
+ qDebug() << "Finished with more than one failure (expected only one).";
+ return false;
+ }
+ } else {
+ qDebug() << "Not finished with failure, but expected.";
+ return false;
+ }
+}
+
+void ClangStaticAnalyzerRunnerTest::runWithTestCodeGeneratedOneIssue()
+{
+ const QString testFilePath = QDir::tempPath() + QLatin1String("/testcode.cpp");
+ const QByteArray source =
+ "void f(int *p) {}\n"
+ "void f2(int *p) {\n"
+ " delete p;\n"
+ " f(p); // warn: use after free\n"
+ "}\n";
+ QVERIFY(writeFile(testFilePath, source));
+
+ QTemporaryDir temporaryDir(QDir::tempPath() + QLatin1String("/qtc-clangstaticanalyzer-XXXXXX"));
+ QVERIFY(temporaryDir.isValid());
+ ClangStaticAnalyzerRunner runner(QLatin1String("clang"), temporaryDir.path(),
+ Utils::Environment::systemEnvironment());
+
+ ClangStaticAnalyzerRunnerSignalTester st(&runner);
+ QVERIFY(runner.run(testFilePath));
+ QVERIFY(st.expectStartedSignal());
+ QVERIFY(st.expectFinishWithSuccessSignal());
+}
+
+void ClangStaticAnalyzerRunnerTest::runWithNonExistentFileToAnalyze()
+{
+ QTemporaryDir temporaryDir(QDir::tempPath() + QLatin1String("/qtc-clangstaticanalyzer-XXXXXX"));
+ QVERIFY(temporaryDir.isValid());
+ ClangStaticAnalyzerRunner runner(QLatin1String("clang"), temporaryDir.path(),
+ Utils::Environment::systemEnvironment());
+
+ ClangStaticAnalyzerRunnerSignalTester st(&runner);
+ QVERIFY(runner.run(QLatin1String("not.existing.file.111")));
+
+ QVERIFY(st.expectStartedSignal());
+ QVERIFY(st.expectFinishWithFailureSignal(finishedWithBadExitCode(1)));
+}
+
+QTEST_MAIN(ClangStaticAnalyzerRunnerTest)
+
+#include "tst_clangstaticanalyzerrunner.moc"
diff --git a/src/plugins/clangstaticanalyzer/tests/tests.pri b/src/plugins/clangstaticanalyzer/tests/tests.pri
new file mode 100644
index 0000000000..8975fc8448
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/tests/tests.pri
@@ -0,0 +1,22 @@
+QTC_LIB_DEPENDS += utils
+QTC_PLUGIN_DEPENDS += analyzerbase
+
+isEmpty(IDE_SOURCE_TREE): IDE_SOURCE_TREE=$$(QTC_SOURCE)
+isEmpty(IDE_BUILD_TREE): IDE_BUILD_TREE=$$(QTC_BUILD)
+isEmpty(IDE_SOURCE_TREE): error(Set QTC_SOURCE environment variable)
+isEmpty(IDE_BUILD_TREE): error(Set QTC_BUILD environment variable)
+
+PLUGINDIR = $$PWD/..
+INCLUDEPATH += $$PLUGINDIR/..
+
+include($$IDE_SOURCE_TREE/qtcreator.pri)
+include($$IDE_SOURCE_TREE/tests/auto/qttestrpath.pri)
+
+QT += testlib
+QT -= gui
+
+CONFIG += console
+CONFIG += testcase
+CONFIG -= app_bundle
+
+TEMPLATE = app
diff --git a/src/plugins/clangstaticanalyzer/tests/tests.pro b/src/plugins/clangstaticanalyzer/tests/tests.pro
new file mode 100644
index 0000000000..72e7c586e9
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/tests/tests.pro
@@ -0,0 +1,6 @@
+TEMPLATE = subdirs
+CONFIG += ordered
+
+SUBDIRS = \
+ clangstaticanalyzerrunner \
+ clangstaticanalyzerlogfilereader
diff --git a/src/plugins/clangstaticanalyzer/tests/tests.qbs b/src/plugins/clangstaticanalyzer/tests/tests.qbs
new file mode 100644
index 0000000000..a6dc94d66c
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/tests/tests.qbs
@@ -0,0 +1,8 @@
+import qbs
+
+Project {
+ references: [
+ "clangstaticanalyzerlogfilereader",
+ "clangstaticanalyzerrunner",
+ ]
+}
diff --git a/src/plugins/clangstaticanalyzer/unit-tests/qt-widgets-app/main.cpp b/src/plugins/clangstaticanalyzer/unit-tests/qt-widgets-app/main.cpp
new file mode 100644
index 0000000000..b48f94ec82
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/unit-tests/qt-widgets-app/main.cpp
@@ -0,0 +1,11 @@
+#include "mainwindow.h"
+#include <QApplication>
+
+int main(int argc, char *argv[])
+{
+ QApplication a(argc, argv);
+ MainWindow w;
+ w.show();
+
+ return a.exec();
+}
diff --git a/src/plugins/clangstaticanalyzer/unit-tests/qt-widgets-app/mainwindow.cpp b/src/plugins/clangstaticanalyzer/unit-tests/qt-widgets-app/mainwindow.cpp
new file mode 100644
index 0000000000..49d64fce7c
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/unit-tests/qt-widgets-app/mainwindow.cpp
@@ -0,0 +1,14 @@
+#include "mainwindow.h"
+#include "ui_mainwindow.h"
+
+MainWindow::MainWindow(QWidget *parent) :
+ QMainWindow(parent),
+ ui(new Ui::MainWindow)
+{
+ ui->setupUi(this);
+}
+
+MainWindow::~MainWindow()
+{
+ delete ui;
+}
diff --git a/src/plugins/clangstaticanalyzer/unit-tests/qt-widgets-app/mainwindow.h b/src/plugins/clangstaticanalyzer/unit-tests/qt-widgets-app/mainwindow.h
new file mode 100644
index 0000000000..ce76956bad
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/unit-tests/qt-widgets-app/mainwindow.h
@@ -0,0 +1,24 @@
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QMainWindow>
+
+QT_BEGIN_NAMESPACE
+namespace Ui {
+class MainWindow;
+}
+QT_END_NAMESPACE
+
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ explicit MainWindow(QWidget *parent = 0);
+ ~MainWindow();
+
+private:
+ Ui::MainWindow *ui;
+};
+
+#endif // MAINWINDOW_H
diff --git a/src/plugins/clangstaticanalyzer/unit-tests/qt-widgets-app/mainwindow.ui b/src/plugins/clangstaticanalyzer/unit-tests/qt-widgets-app/mainwindow.ui
new file mode 100644
index 0000000000..6050363fa7
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/unit-tests/qt-widgets-app/mainwindow.ui
@@ -0,0 +1,24 @@
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>300</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>MainWindow</string>
+ </property>
+ <widget class="QMenuBar" name="menuBar" />
+ <widget class="QToolBar" name="mainToolBar" />
+ <widget class="QWidget" name="centralWidget" />
+ <widget class="QStatusBar" name="statusBar" />
+ </widget>
+ <layoutDefault spacing="6" margin="11" />
+ <pixmapfunction></pixmapfunction>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/plugins/clangstaticanalyzer/unit-tests/qt-widgets-app/qt-widgets-app.pro b/src/plugins/clangstaticanalyzer/unit-tests/qt-widgets-app/qt-widgets-app.pro
new file mode 100644
index 0000000000..d9e1f2590b
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/unit-tests/qt-widgets-app/qt-widgets-app.pro
@@ -0,0 +1,8 @@
+QT += core gui
+greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
+
+TARGET = qt-widgets-app
+TEMPLATE = app
+SOURCES += main.cpp mainwindow.cpp
+HEADERS += mainwindow.h
+FORMS += mainwindow.ui
diff --git a/src/plugins/clangstaticanalyzer/unit-tests/qt-widgets-app/qt-widgets-app.qbs b/src/plugins/clangstaticanalyzer/unit-tests/qt-widgets-app/qt-widgets-app.qbs
new file mode 100644
index 0000000000..e641f9bd54
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/unit-tests/qt-widgets-app/qt-widgets-app.qbs
@@ -0,0 +1,17 @@
+import qbs 1.0
+
+QtApplication {
+ name : "Qt Widgets Application"
+
+ Depends {
+ name: "Qt"
+ submodules: [ "widgets" ]
+ }
+
+ files : [
+ "main.cpp",
+ "mainwindow.cpp",
+ "mainwindow.h",
+ "mainwindow.ui"
+ ]
+}
diff --git a/src/plugins/clangstaticanalyzer/unit-tests/simple/main.cpp b/src/plugins/clangstaticanalyzer/unit-tests/simple/main.cpp
new file mode 100644
index 0000000000..af917a3a33
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/unit-tests/simple/main.cpp
@@ -0,0 +1,5 @@
+int main()
+{
+ int *i = 0;
+ *i = 42;
+}
diff --git a/src/plugins/clangstaticanalyzer/unit-tests/simple/simple.pro b/src/plugins/clangstaticanalyzer/unit-tests/simple/simple.pro
new file mode 100644
index 0000000000..fb560c2af6
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/unit-tests/simple/simple.pro
@@ -0,0 +1,3 @@
+CONFIG -= QT
+
+SOURCES = main.cpp
diff --git a/src/plugins/clangstaticanalyzer/unit-tests/simple/simple.qbs b/src/plugins/clangstaticanalyzer/unit-tests/simple/simple.qbs
new file mode 100644
index 0000000000..f6ae698a0c
--- /dev/null
+++ b/src/plugins/clangstaticanalyzer/unit-tests/simple/simple.qbs
@@ -0,0 +1,5 @@
+import qbs
+
+CppApplication {
+ files: ["main.cpp"]
+}