summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikolai Kosjar <nikolai.kosjar@theqtcompany.com>2014-09-25 11:11:58 +0200
committerEike Ziller <eike.ziller@theqtcompany.com>2014-10-16 13:36:09 +0300
commitb9f9eb7ae5d84c2b1200becc66680a4a8cda9e94 (patch)
tree283778fabb840733cbb0360e8347bab42674758d
parentb3e07a53dbaa3ae9c89971da6ac2eaf044faf4c5 (diff)
downloadqt-creator-b9f9eb7ae5d84c2b1200becc66680a4a8cda9e94.tar.gz
Import Clang Static Analyzer plugin
This plugin adds "Clang Static Analyzer" to the Analyze mode, which processes all implementation/source project files of the current project. For this, it will call the clang executable for each file. The found diagnostics will be displayed in a view similar to the one used in "Valgrind Memory Analyzer". The user can specify the clang executable to use and the number of concurrent processes to launch in Menu: Tools > Options > Analyzer > Clang Static Analyzer. Main TODOs: * Fiddle around the appropriate command line options, currently only defines and include paths are passed on. * Tests on Windows / OS X. * Remove dependency to clangcodemodel by moving the functions that create command line arguments to CppTools. Mostly they are not even specific to clang (but would also work with gcc). * Maybe limit to a range of tested clang versions. * How to deal with directory containing all the log files after the user starts a new run or Creator is shut down? (delete it? leave it there? make it configurable?). * Find out how to properly integrate the tests. Imaginable future additions: * Adding a button to load result/log files from a directory, e.g. if the user used the 'scan-build' approach. * Adding a button with a filter menu in order to display only diagnostics from certain categories, similar to "Valgrind Memory Analyzer". Change-Id: I6aeb5dfdbdfa239a06c03dd8759a983df71b77ea Reviewed-by: Eike Ziller <eike.ziller@theqtcompany.com>
-rw-r--r--.gitignore2
-rw-r--r--clangstaticanalyzer.pro6
-rw-r--r--plugins/clangstaticanalyzer/ClangStaticAnalyzer.json.in15
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzer.pro42
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzer_dependencies.pri9
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzer_global.h31
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzerconfigwidget.cpp58
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzerconfigwidget.h48
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzerconfigwidget.ui99
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzerconstants.h31
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzerdiagnostic.cpp50
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzerdiagnostic.h73
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticmodel.cpp121
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticmodel.h50
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticview.cpp223
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticview.h43
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzerlogfilereader.cpp374
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzerlogfilereader.h38
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzerplugin.cpp161
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzerplugin.h52
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.cpp235
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.h81
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzerruncontrolfactory.cpp62
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzerruncontrolfactory.h45
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzerrunner.cpp165
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzerrunner.h71
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzersettings.cpp98
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzersettings.h54
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzertool.cpp187
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzertool.h63
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzerutils.cpp75
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzerutils.h43
-rw-r--r--plugins/clangstaticanalyzer/tests/clangstaticanalyzerlogfilereader/clangstaticanalyzerlogfilereader.pro14
-rw-r--r--plugins/clangstaticanalyzer/tests/clangstaticanalyzerlogfilereader/data/noDiagnostics.plist14
-rw-r--r--plugins/clangstaticanalyzer/tests/clangstaticanalyzerlogfilereader/data/someDiagnostics.plist162
-rw-r--r--plugins/clangstaticanalyzer/tests/clangstaticanalyzerlogfilereader/tst_clangstaticanalyzerlogfilereader.cpp175
-rw-r--r--plugins/clangstaticanalyzer/tests/clangstaticanalyzerrunner/clangstaticanalyzerrunner.pro11
-rw-r--r--plugins/clangstaticanalyzer/tests/clangstaticanalyzerrunner/tst_clangstaticanalyzerrunner.cpp167
-rw-r--r--plugins/clangstaticanalyzer/tests/tests.pri21
-rw-r--r--plugins/clangstaticanalyzer/tests/tests.pro6
-rw-r--r--qtcreatorlibrary.pri7
-rw-r--r--qtcreatorplugin.pri8
42 files changed, 3290 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000..444078f3cd
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+*.pro.user
+
diff --git a/clangstaticanalyzer.pro b/clangstaticanalyzer.pro
new file mode 100644
index 0000000000..5a433f2bd4
--- /dev/null
+++ b/clangstaticanalyzer.pro
@@ -0,0 +1,6 @@
+TEMPLATE = subdirs
+CONFIG += ordered
+
+SUBDIRS += plugins/clangstaticanalyzer
+
+QMAKE_EXTRA_TARGETS = docs install_docs # dummy targets for consistency
diff --git a/plugins/clangstaticanalyzer/ClangStaticAnalyzer.json.in b/plugins/clangstaticanalyzer/ClangStaticAnalyzer.json.in
new file mode 100644
index 0000000000..0203fe1f02
--- /dev/null
+++ b/plugins/clangstaticanalyzer/ClangStaticAnalyzer.json.in
@@ -0,0 +1,15 @@
+{
+ \"Name\" : \"ClangStaticAnalyzer\",
+ \"Version\" : \"$$QTCREATOR_VERSION\",
+ \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\",
+ \"Vendor\" : \"Digia Plc\",
+ \"Copyright\" : \"(C) 2014 Digia Plc\",
+ \"License\" : [ \"Commercial Usage\",
+ \"\",
+ \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt Commercial License Agreement provided with the Software or, alternatively, in accordance with the terms contained in a written agreement between you and Digia.\"
+ ],
+ \"Category\" : \"Code Analyzer\",
+ \"Description\" : \"ClangStaticAnalyzer Plugin.\",
+ \"Url\" : \"http://qt.digia.com\",
+ $$dependencyList
+}
diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzer.pro b/plugins/clangstaticanalyzer/clangstaticanalyzer.pro
new file mode 100644
index 0000000000..22c94b2bf6
--- /dev/null
+++ b/plugins/clangstaticanalyzer/clangstaticanalyzer.pro
@@ -0,0 +1,42 @@
+TARGET = ClangStaticAnalyzer
+TEMPLATE = lib
+
+PROVIDER = Digia
+include(../../qtcreatorplugin.pri)
+include(clangstaticanalyzer_dependencies.pri)
+
+SOURCES += \
+ clangstaticanalyzerconfigwidget.cpp \
+ clangstaticanalyzerdiagnostic.cpp \
+ clangstaticanalyzerdiagnosticmodel.cpp \
+ clangstaticanalyzerdiagnosticview.cpp \
+ clangstaticanalyzerlogfilereader.cpp \
+ clangstaticanalyzerplugin.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 \
+ clangstaticanalyzerruncontrolfactory.h \
+ clangstaticanalyzerruncontrol.h \
+ clangstaticanalyzerrunner.h \
+ clangstaticanalyzersettings.h \
+ clangstaticanalyzertool.h \
+ clangstaticanalyzerutils.h
+
+FORMS += \
+ clangstaticanalyzerconfigwidget.ui
+
+DISTFILES += \
+ tests/tests.pri
diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzer_dependencies.pri b/plugins/clangstaticanalyzer/clangstaticanalyzer_dependencies.pri
new file mode 100644
index 0000000000..8579739916
--- /dev/null
+++ b/plugins/clangstaticanalyzer/clangstaticanalyzer_dependencies.pri
@@ -0,0 +1,9 @@
+QTC_PLUGIN_NAME = ClangStaticAnalyzer
+QTC_LIB_DEPENDS += \
+ extensionsystem \
+ utils
+QTC_PLUGIN_DEPENDS += \
+ analyzerbase \
+ clangcodemodel \
+ cpptools \
+ licensechecker
diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzer_global.h b/plugins/clangstaticanalyzer/clangstaticanalyzer_global.h
new file mode 100644
index 0000000000..a3fc0a8b85
--- /dev/null
+++ b/plugins/clangstaticanalyzer/clangstaticanalyzer_global.h
@@ -0,0 +1,31 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+** This file is part of the Qt Enterprise Qt Quick Profiler Add-on.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+****************************************************************************/
+
+#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/plugins/clangstaticanalyzer/clangstaticanalyzerconfigwidget.cpp b/plugins/clangstaticanalyzer/clangstaticanalyzerconfigwidget.cpp
new file mode 100644
index 0000000000..c0ae86af94
--- /dev/null
+++ b/plugins/clangstaticanalyzer/clangstaticanalyzerconfigwidget.cpp
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+** This file is part of the Qt Enterprise LicenseChecker Add-on.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+****************************************************************************/
+
+#include "clangstaticanalyzerconfigwidget.h"
+#include "ui_clangstaticanalyzerconfigwidget.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);
+
+ m_ui->clangExecutableChooser->setExpectedKind(Utils::PathChooser::ExistingCommand);
+ m_ui->clangExecutableChooser->setHistoryCompleter(
+ QLatin1String("ClangStaticAnalyzer.ClangCommand.History"));
+ m_ui->clangExecutableChooser->setPromptDialogTitle(tr("Clang Command"));
+ m_ui->clangExecutableChooser->setPath(settings->clangExecutable());
+ connect(m_ui->clangExecutableChooser, &Utils::PathChooser::changed,
+ m_settings, &ClangStaticAnalyzerSettings::setClangExecutable);
+
+ 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),
+ m_settings, &ClangStaticAnalyzerSettings::setSimultaneousProcesses);
+}
+
+ClangStaticAnalyzerConfigWidget::~ClangStaticAnalyzerConfigWidget()
+{
+ delete m_ui;
+}
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzer
diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerconfigwidget.h b/plugins/clangstaticanalyzer/clangstaticanalyzerconfigwidget.h
new file mode 100644
index 0000000000..939766622a
--- /dev/null
+++ b/plugins/clangstaticanalyzer/clangstaticanalyzerconfigwidget.h
@@ -0,0 +1,48 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+** This file is part of the Qt Enterprise LicenseChecker Add-on.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+****************************************************************************/
+
+#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/plugins/clangstaticanalyzer/clangstaticanalyzerconfigwidget.ui b/plugins/clangstaticanalyzer/clangstaticanalyzerconfigwidget.ui
new file mode 100644
index 0000000000..800733a331
--- /dev/null
+++ b/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/plugins/clangstaticanalyzer/clangstaticanalyzerconstants.h b/plugins/clangstaticanalyzer/clangstaticanalyzerconstants.h
new file mode 100644
index 0000000000..85a05087a6
--- /dev/null
+++ b/plugins/clangstaticanalyzer/clangstaticanalyzerconstants.h
@@ -0,0 +1,31 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+** This file is part of the Qt Enterprise Qt LicenseChecker Add-on.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+****************************************************************************/
+
+#ifndef CLANGSTATICANALYZERCONSTANTS_H
+#define CLANGSTATICANALYZERCONSTANTS_H
+
+namespace ClangStaticAnalyzer {
+namespace Constants {
+
+const char CLANG_EXECUTABLE_BASE_NAME[] = "clang";
+const char SETTINGS_ID[] = "ClangStaticAnalyzer";
+
+} // Constants
+} // ClangStaticAnalyzer
+
+#endif // CLANGSTATICANALYZERCONSTANTS_H
diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnostic.cpp b/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnostic.cpp
new file mode 100644
index 0000000000..e9152ffa47
--- /dev/null
+++ b/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnostic.cpp
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+** This file is part of the Qt Enterprise LicenseChecker Add-on.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+****************************************************************************/
+
+#include "clangstaticanalyzerdiagnostic.h"
+
+namespace ClangStaticAnalyzer {
+namespace Internal {
+
+Location::Location()
+ : line(0), column(0)
+{
+}
+
+Location::Location(const QString &filePath, int line, int column)
+ : filePath(filePath), line(line), column(column)
+{
+}
+
+bool Location::isValid() const
+{
+ return !filePath.isEmpty() && line >= 0 && column >= 0;
+}
+
+ExplainingStep::ExplainingStep()
+ : depth(0)
+{
+}
+
+bool ExplainingStep::isValid() const
+{
+ return location.isValid() && !ranges.isEmpty() && !message.isEmpty();
+}
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzer
diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnostic.h b/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnostic.h
new file mode 100644
index 0000000000..3348cfa416
--- /dev/null
+++ b/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnostic.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+** This file is part of the Qt Enterprise LicenseChecker Add-on.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+****************************************************************************/
+
+#ifndef CLANGSTATICANALZYERDIAGNOSTIC_H
+#define CLANGSTATICANALZYERDIAGNOSTIC_H
+
+#include <QList>
+#include <QMetaType>
+#include <QString>
+
+namespace ClangStaticAnalyzer {
+namespace Internal {
+
+class Location
+{
+public:
+ Location();
+ Location(const QString &filePath, int line, int column);
+
+ bool isValid() const;
+
+ QString filePath;
+ int line;
+ int column;
+};
+
+class ExplainingStep
+{
+public:
+ ExplainingStep();
+
+ bool isValid() const;
+
+ QString message;
+ QString extendedMessage;
+ Location location;
+ QList<Location> ranges;
+ int depth;
+};
+
+class Diagnostic
+{
+public:
+ QString description;
+ QString category;
+ QString type;
+ QString issueContextKind;
+ QString issueContext;
+ Location location;
+ QList<ExplainingStep> explainingSteps;
+};
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzer
+
+Q_DECLARE_METATYPE(ClangStaticAnalyzer::Internal::Diagnostic)
+
+#endif // CLANGSTATICANALZYERDIAGNOSTIC_H
diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticmodel.cpp b/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticmodel.cpp
new file mode 100644
index 0000000000..2318c50c05
--- /dev/null
+++ b/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticmodel.cpp
@@ -0,0 +1,121 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+** This file is part of the Qt Enterprise LicenseChecker Add-on.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+****************************************************************************/
+
+#include "clangstaticanalyzerdiagnosticmodel.h"
+
+#include "clangstaticanalyzerutils.h"
+
+#include <QCoreApplication>
+
+namespace ClangStaticAnalyzer {
+namespace Internal {
+
+ClangStaticAnalyzerDiagnosticModel::ClangStaticAnalyzerDiagnosticModel(QObject *parent)
+ : QAbstractListModel(parent)
+{
+}
+
+void ClangStaticAnalyzerDiagnosticModel::addDiagnostics(const QList<Diagnostic> &diagnostics)
+{
+ beginInsertRows(QModelIndex(), m_diagnostics.size(),
+ m_diagnostics.size() + diagnostics.size() - 1 );
+ m_diagnostics += diagnostics;
+ endInsertRows();
+}
+
+void ClangStaticAnalyzerDiagnosticModel::clear()
+{
+ beginResetModel();
+ m_diagnostics.clear();
+ endResetModel();
+}
+
+int ClangStaticAnalyzerDiagnosticModel::rowCount(const QModelIndex &parent) const
+{
+ return parent.isValid() ? 0 : m_diagnostics.count();
+}
+
+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);
+ }
+
+ if (!diagnostic.type.isEmpty()) {
+ lines << qMakePair(
+ QCoreApplication::translate("ClangStaticAnalyzer::Diagnostic", "Type:"),
+ diagnostic.type);
+ }
+
+ if (!diagnostic.issueContext.isEmpty() && !diagnostic.issueContextKind.isEmpty()) {
+ lines << qMakePair(
+ QCoreApplication::translate("ClangStaticAnalyzer::Diagnostic", "Context:"),
+ diagnostic.issueContextKind + QLatin1Char(' ') + diagnostic.issueContext);
+ }
+
+ 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;
+}
+
+QVariant ClangStaticAnalyzerDiagnosticModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+
+ if (index.parent().isValid())
+ return QVariant();
+
+ const int row = index.row();
+ if (row < 0 || row >= m_diagnostics.size())
+ return QVariant();
+
+ const Diagnostic diagnostic = m_diagnostics.at(row);
+
+ if (role == Qt::DisplayRole)
+ return QString(QLatin1String("Some specific diagnostic")); // TODO: Remove?
+ else if (role == Qt::ToolTipRole)
+ return createDiagnosticToolTipString(diagnostic);
+ else if (role == Qt::UserRole)
+ return QVariant::fromValue<Diagnostic>(diagnostic);
+
+ return QVariant();
+}
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzer
diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticmodel.h b/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticmodel.h
new file mode 100644
index 0000000000..beccf89a84
--- /dev/null
+++ b/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticmodel.h
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+** This file is part of the Qt Enterprise LicenseChecker Add-on.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+****************************************************************************/
+
+#ifndef CLANGSTATICANALYZERDIAGNOSTICMODEL_H
+#define CLANGSTATICANALYZERDIAGNOSTICMODEL_H
+
+#include "clangstaticanalyzerlogfilereader.h"
+
+#include <QAbstractListModel>
+
+namespace ClangStaticAnalyzer {
+namespace Internal {
+
+class ClangStaticAnalyzerDiagnosticModel : public QAbstractListModel
+{
+ Q_OBJECT
+
+public:
+ ClangStaticAnalyzerDiagnosticModel(QObject *parent = 0);
+
+ void addDiagnostics(const QList<Diagnostic> &diagnostics);
+ void clear();
+
+ // QAbstractListModel interface
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ QVariant data(const QModelIndex &index, int role) const;
+
+private:
+ QList<Diagnostic> m_diagnostics;
+};
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzer
+
+#endif // CLANGSTATICANALYZERDIAGNOSTICMODEL_H
diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticview.cpp b/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticview.cpp
new file mode 100644
index 0000000000..fd9e216ed7
--- /dev/null
+++ b/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticview.cpp
@@ -0,0 +1,223 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+** This file is part of the Qt Enterprise LicenseChecker Add-on.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+****************************************************************************/
+
+#include "clangstaticanalyzerdiagnosticview.h"
+
+#include "clangstaticanalyzerlogfilereader.h"
+#include "clangstaticanalyzerutils.h"
+
+#include <utils/qtcassert.h>
+
+#include <QCoreApplication>
+#include <QDebug>
+#include <QFileInfo>
+#include <QLabel>
+#include <QVBoxLayout>
+
+using namespace Analyzer;
+
+namespace {
+
+QLabel *createCommonLabel()
+{
+ QLabel *label = new QLabel;
+ label->setWordWrap(true);
+ label->setContentsMargins(0, 0, 0, 0);
+ label->setMargin(0);
+ label->setIndent(10);
+ return label;
+}
+
+QString createSummaryText(const ClangStaticAnalyzer::Internal::Diagnostic &diagnostic,
+ const QPalette &palette)
+{
+ const QColor color = palette.color(QPalette::Text);
+ const QString linkStyle = QString::fromLatin1("style=\"color:rgba(%1, %2, %3, %4);\"")
+ .arg(color.red())
+ .arg(color.green())
+ .arg(color.blue())
+ .arg(int(0.7 * 255));
+ const QString fileName = QFileInfo(diagnostic.location.filePath).fileName();
+ const QString location = fileName + QLatin1Char(' ')
+ + QString::number(diagnostic.location.line);
+ return QString::fromLatin1("%1&nbsp;&nbsp;<span %3>%2</span>")
+ .arg(diagnostic.description, location, linkStyle);
+}
+
+QLabel *createSummaryLabel(const ClangStaticAnalyzer::Internal::Diagnostic &diagnostic)
+{
+ QLabel *label = createCommonLabel();
+ QPalette palette = label->palette();
+ palette.setBrush(QPalette::Text, palette.highlightedText());
+ label->setPalette(palette);
+ label->setText(createSummaryText(diagnostic, palette));
+ return label;
+}
+
+QLabel *createExplainingStepLabel(const QFont &font, bool useAlternateRowPalette)
+{
+ QLabel *label = createCommonLabel();
+
+ // Font
+ QFont fixedPitchFont = font;
+ fixedPitchFont.setFixedPitch(true);
+ label->setFont(fixedPitchFont);
+
+ // Background
+ label->setAutoFillBackground(true);
+ if (useAlternateRowPalette) {
+ QPalette p = label->palette();
+ p.setBrush(QPalette::Base, p.alternateBase());
+ label->setPalette(p);
+ }
+
+ return label;
+}
+
+QString createLocationString(const ClangStaticAnalyzer::Internal::Location &location,
+ bool withMarkup)
+{
+ const QString filePath = location.filePath;
+ const QString fileName = QFileInfo(filePath).fileName();
+ const QString lineNumber = QString::number(location.line);
+ const QString columnNumber = QString::number(location.column - 1);
+ const QString fileNameAndLine = fileName + QLatin1Char(':') + lineNumber;
+
+ if (withMarkup) {
+ return QLatin1String("in <a href=\"file://")
+ + filePath + QLatin1Char(':') + lineNumber + QLatin1Char(':') + columnNumber
+ + QLatin1String("\">")
+ + fileNameAndLine
+ + QLatin1String("</a>");
+ } else {
+ return QLatin1String("in ") + fileNameAndLine;
+ }
+}
+
+QString createExplainingStepNumberString(int number)
+{
+ const int fieldWidth = 2;
+ return QString::fromLatin1("<code style='white-space:pre'>%1:</code>").arg(number, fieldWidth);
+}
+
+QString createExplainingStepToolTipString(const ClangStaticAnalyzer::Internal::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);
+ }
+ if (!step.extendedMessage.isEmpty()) {
+ lines << qMakePair(
+ QCoreApplication::translate("ClangStaticAnalyzer::ExplainingStep", "Extended Message:"),
+ step.extendedMessage);
+ }
+
+ 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;
+}
+
+} // anonymous namespace
+
+namespace ClangStaticAnalyzer {
+namespace Internal {
+
+ClangStaticAnalyzerDiagnosticDelegate::ClangStaticAnalyzerDiagnosticDelegate(QListView *parent)
+ : DetailedErrorDelegate(parent)
+{
+}
+
+DetailedErrorDelegate::SummaryLineInfo ClangStaticAnalyzerDiagnosticDelegate::summaryInfo(
+ const QModelIndex &index) const
+{
+ const Diagnostic diagnostic = index.data(Qt::UserRole).value<Diagnostic>();
+
+ DetailedErrorDelegate::SummaryLineInfo info;
+ info.errorText = diagnostic.description;
+ info.errorLocation = createLocationString(diagnostic.location, /*withMarkup=*/ false);
+ return info;
+}
+
+void ClangStaticAnalyzerDiagnosticDelegate::copy()
+{
+ qDebug() << Q_FUNC_INFO;
+}
+
+QWidget *ClangStaticAnalyzerDiagnosticDelegate::createDetailsWidget(const QFont &font,
+ const QModelIndex &index,
+ QWidget *parent) const
+{
+ QWidget *widget = new QWidget(parent);
+ QVBoxLayout *layout = new QVBoxLayout;
+
+ const Diagnostic diagnostic = index.data(Qt::UserRole).value<Diagnostic>();
+
+ // Add summary label
+ QLabel *summaryLineLabel = createSummaryLabel(diagnostic);
+ connect(summaryLineLabel, &QLabel::linkActivated,
+ this, &ClangStaticAnalyzerDiagnosticDelegate::openLinkInEditor);
+ layout->addWidget(summaryLineLabel);
+
+ // Add labels for explaining steps
+ int explainingStepNumber = 1;
+ foreach (const ExplainingStep &explainingStep, diagnostic.explainingSteps) {
+ const QString text = createExplainingStepNumberString(explainingStepNumber++)
+ + QLatin1Char(' ')
+ + explainingStep.extendedMessage
+ + QLatin1Char(' ')
+ + createLocationString(explainingStep.location, /*withMarkup=*/ true);
+
+ QLabel *label = createExplainingStepLabel(font, explainingStepNumber % 2 == 0);
+ label->setParent(widget);
+ label->setText(text);
+ label->setToolTip(createExplainingStepToolTipString(explainingStep));
+ connect(label, &QLabel::linkActivated,
+ this, &ClangStaticAnalyzerDiagnosticDelegate::openLinkInEditor);
+ layout->addWidget(label);
+ }
+
+ layout->setContentsMargins(0, 0, 0, 0);
+ layout->setSpacing(0);
+ widget->setLayout(layout);
+ return widget;
+}
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzer
diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticview.h b/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticview.h
new file mode 100644
index 0000000000..551aff3e72
--- /dev/null
+++ b/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticview.h
@@ -0,0 +1,43 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+** This file is part of the Qt Enterprise LicenseChecker Add-on.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+****************************************************************************/
+
+#ifndef CLANGSTATICANALYZERDIAGNOSTICVIEW_H
+#define CLANGSTATICANALYZERDIAGNOSTICVIEW_H
+
+#include <analyzerbase/detailederrorview.h>
+
+namespace ClangStaticAnalyzer {
+namespace Internal {
+
+class ClangStaticAnalyzerDiagnosticDelegate : public Analyzer::DetailedErrorDelegate
+{
+public:
+ ClangStaticAnalyzerDiagnosticDelegate(QListView *parent);
+
+ SummaryLineInfo summaryInfo(const QModelIndex &index) const;
+ void copy();
+
+private:
+ QWidget *createDetailsWidget(const QFont &font, const QModelIndex &index,
+ QWidget *parent) const;
+};
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzer
+
+#endif // CLANGSTATICANALYZERDIAGNOSTICVIEW_H
diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerlogfilereader.cpp b/plugins/clangstaticanalyzer/clangstaticanalyzerlogfilereader.cpp
new file mode 100644
index 0000000000..22bbb4cb73
--- /dev/null
+++ b/plugins/clangstaticanalyzer/clangstaticanalyzerlogfilereader.cpp
@@ -0,0 +1,374 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+** This file is part of the Qt Enterprise LicenseChecker Add-on.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+****************************************************************************/
+
+#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();
+ Location readLocationDict(bool elementIsRead = false);
+ QList<Location> 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;
+
+ 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;
+}
+
+Location ClangStaticAnalyzerLogFileReader::readLocationDict(bool elementIsRead)
+{
+ Location 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, columnOk, fileIndexOk;
+
+ // 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 = Location(m_referencedFiles.at(fileIndex), line, column);
+ }
+ return location;
+}
+
+QList<Location> ClangStaticAnalyzerLogFileReader::readRangesArray()
+{
+ QList<Location> 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/plugins/clangstaticanalyzer/clangstaticanalyzerlogfilereader.h b/plugins/clangstaticanalyzer/clangstaticanalyzerlogfilereader.h
new file mode 100644
index 0000000000..fe8f5075ff
--- /dev/null
+++ b/plugins/clangstaticanalyzer/clangstaticanalyzerlogfilereader.h
@@ -0,0 +1,38 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+** This file is part of the Qt Enterprise LicenseChecker Add-on.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+****************************************************************************/
+
+#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/plugins/clangstaticanalyzer/clangstaticanalyzerplugin.cpp b/plugins/clangstaticanalyzer/clangstaticanalyzerplugin.cpp
new file mode 100644
index 0000000000..0a321d64e8
--- /dev/null
+++ b/plugins/clangstaticanalyzer/clangstaticanalyzerplugin.cpp
@@ -0,0 +1,161 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+** This file is part of the Qt Enterprise Qt Quick Profiler Add-on.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+****************************************************************************/
+
+#include "clangstaticanalyzerplugin.h"
+
+#include "clangstaticanalyzerconfigwidget.h"
+#include "clangstaticanalyzerruncontrolfactory.h"
+#include "clangstaticanalyzertool.h"
+
+#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 <licensechecker/licensecheckerplugin.h>
+
+#include <extensionsystem/pluginmanager.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.
+
+ LicenseChecker::LicenseCheckerPlugin *licenseChecker
+ = ExtensionSystem::PluginManager::getObject<LicenseChecker::LicenseCheckerPlugin>();
+
+ if (licenseChecker && licenseChecker->hasValidLicense()) {
+ if (licenseChecker->enterpriseFeatures())
+ return initializeEnterpriseFeatures(arguments, errorString);
+ } else {
+ qWarning() << "Invalid license, disabling Clang Static Analyzer";
+ }
+
+ return true;
+}
+
+bool ClangStaticAnalyzerPlugin::initializeEnterpriseFeatures(const QStringList &arguments,
+ QString *errorString)
+{
+ Q_UNUSED(arguments);
+ Q_UNUSED(errorString);
+
+ addAutoReleasedObject(new ClangStaticAnalyzerOptionsPage);
+ addAutoReleasedObject(new ClangStaticAnalyzerRunControlFactory);
+
+ m_analyzerTool = new ClangStaticAnalyzerTool(this);
+
+ const QString toolTip = tr("Clang Static Analyzer uses the analyzer from the clang project "
+ "to find bugs.");
+
+ AnalyzerAction *action = new AnalyzerAction(this);
+ action->setId("ClangStaticAnalyzer");
+ action->setTool(m_analyzerTool);
+ action->setText(tr("Clang Static Analyzer"));
+ action->setToolTip(toolTip);
+ action->setMenuGroup(Constants::G_ANALYZER_TOOLS);
+ action->setStartMode(StartLocal);
+ 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;
+}
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzerPlugin
diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerplugin.h b/plugins/clangstaticanalyzer/clangstaticanalyzerplugin.h
new file mode 100644
index 0000000000..d96611a16f
--- /dev/null
+++ b/plugins/clangstaticanalyzer/clangstaticanalyzerplugin.h
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+** This file is part of the Qt Enterprise Qt Quick Profiler Add-on.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+****************************************************************************/
+
+#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:
+ ClangStaticAnalyzerTool *m_analyzerTool;
+};
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzerPlugin
+
+#endif // CLANGSTATICANALYZERPLUGIN_H
+
diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.cpp b/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.cpp
new file mode 100644
index 0000000000..b459f5c022
--- /dev/null
+++ b/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.cpp
@@ -0,0 +1,235 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+** This file is part of the Qt Enterprise LicenseChecker Add-on.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+****************************************************************************/
+
+#include "clangstaticanalyzerruncontrol.h"
+
+#include "clangstaticanalyzerlogfilereader.h"
+#include "clangstaticanalyzerrunner.h"
+#include "clangstaticanalyzersettings.h"
+#include "clangstaticanalyzerutils.h"
+
+#include <analyzerbase/analyzermanager.h>
+
+#include <clangcodemodel/clangutils.h>
+
+#include <coreplugin/progressmanager/futureprogress.h>
+#include <coreplugin/progressmanager/progressmanager.h>
+
+#include <cpptools/cppmodelmanager.h>
+#include <cpptools/cppprojectfile.h>
+
+#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/project.h>
+
+#include <QLoggingCategory>
+#include <QTemporaryDir>
+
+using namespace CppTools;
+using namespace ProjectExplorer;
+
+static Q_LOGGING_CATEGORY(LOG, "qt.clangstaticanalyzer.runcontrol")
+
+namespace ClangStaticAnalyzer {
+namespace Internal {
+
+ClangStaticAnalyzerRunControl::ClangStaticAnalyzerRunControl(
+ const Analyzer::AnalyzerStartParameters &startParams,
+ ProjectExplorer::RunConfiguration *runConfiguration)
+ : AnalyzerRunControl(startParams, runConfiguration)
+ , m_initialFilesToProcessSize(0)
+ , m_runningProcesses(0)
+{
+}
+
+static QList<ClangStaticAnalyzerRunControl::FileConfiguration> calculateFilesToProcess(
+ Project *project)
+{
+ typedef ClangStaticAnalyzerRunControl::FileConfiguration ProjectFileConfiguration;
+ QTC_ASSERT(project, return QList<ProjectFileConfiguration>());
+ ProjectInfo projectInfo = CppModelManager::instance()->projectInfo(project);
+ QTC_ASSERT(projectInfo, return QList<ProjectFileConfiguration>());
+
+ QList<ProjectFileConfiguration> files;
+ const QList<ProjectPart::Ptr> projectParts = projectInfo.projectParts();
+ foreach (const ProjectPart::Ptr &projectPart, projectParts) {
+ foreach (const ProjectFile &file, projectPart->files) {
+ if (file.path == CppModelManager::configurationFileName())
+ continue;
+ QTC_CHECK(file.kind != ProjectFile::Unclassified);
+ if (ProjectFile::isSource(file.kind))
+ files << ProjectFileConfiguration(file.path, projectPart);
+ }
+ }
+
+ return files;
+}
+
+bool ClangStaticAnalyzerRunControl::startEngine()
+{
+ emit starting(this);
+
+ Project *currentProject = ProjectExplorerPlugin::currentProject();
+ QTC_ASSERT(currentProject, emit finished(); return false);
+
+ // Check clang executable
+ bool isValidClangExecutable;
+ const QString executable = clangExecutableFromSettings(&isValidClangExecutable);
+ if (!isValidClangExecutable) {
+ emit appendMessage(tr("Clang Static Analyzer: Invalid executable \"%1\", stop.\n")
+ .arg(executable),
+ Utils::ErrorMessageFormat);
+ emit finished();
+ return false;
+ }
+ m_clangExecutable = executable;
+
+ // Create log dir
+ QTemporaryDir temporaryDir(QDir::tempPath() + QLatin1String("/qtc-clangstaticanalyzer-XXXXXX"));
+ temporaryDir.setAutoRemove(false);
+ if (!temporaryDir.isValid()) {
+ emit appendMessage(tr("Clang Static Analyzer: Failed to create temporary dir, stop.\n"),
+ Utils::ErrorMessageFormat);
+ emit finished();
+ return false;
+ }
+ m_clangLogFileDir = temporaryDir.path();
+
+ // Collect files
+ const QList<FileConfiguration> filesToProcess = calculateFilesToProcess(currentProject);
+ qCDebug(LOG()) << "Files to process:";
+ foreach (const FileConfiguration &fileConfig, filesToProcess) {
+ qCDebug(LOG()) << fileConfig.filePath + QLatin1String(" [")
+ + fileConfig.projectPart->projectFile + QLatin1Char(']');
+ }
+ m_filesToProcess = filesToProcess;
+ m_initialFilesToProcessSize = m_filesToProcess.count();
+
+ // Set up progress information
+ using namespace Core;
+ 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)
+ const int parallelRuns = ClangStaticAnalyzerSettings::instance()->simultaneousProcesses();
+ QTC_ASSERT(parallelRuns >= 1, emit finished(); return false);
+ m_runningProcesses = 0;
+ while (m_runningProcesses < parallelRuns && !m_filesToProcess.isEmpty())
+ analyzeNextFile();
+ return true;
+}
+
+void ClangStaticAnalyzerRunControl::stopEngine()
+{
+ m_filesToProcess.clear();
+}
+
+QStringList createDefinesAndIncludesOptions(const ProjectPart::Ptr projectPart)
+{
+ QStringList result;
+ result += ClangCodeModel::Utils::createDefineOptions(projectPart->projectDefines, false);
+ result += ClangCodeModel::Utils::createHeaderPathOptions(projectPart->headerPaths);
+ result += QLatin1String("-fPIC"); // TODO: Remove?
+ return result;
+}
+
+void ClangStaticAnalyzerRunControl::analyzeNextFile()
+{
+ if (m_progress.isFinished())
+ return; // The previous call already reported that we are finished.
+
+ if (m_filesToProcess.isEmpty()) {
+ QTC_ASSERT(m_runningProcesses >= 0, return);
+ if (m_runningProcesses == 0) {
+ m_progress.reportFinished();
+ emit finished();
+ }
+ return;
+ }
+
+ const FileConfiguration config = m_filesToProcess.takeFirst();
+ const QString filePath = config.filePath;
+ const QStringList definesAndIncludesOptions
+ = createDefinesAndIncludesOptions(config.projectPart);
+
+ ClangStaticAnalyzerRunner *runner = createRunner();
+ qCDebug(LOG) << "analyzeNextFile:" << filePath;
+ QTC_ASSERT(runner->run(filePath, definesAndIncludesOptions), return);
+ ++m_runningProcesses;
+}
+
+ClangStaticAnalyzerRunner *ClangStaticAnalyzerRunControl::createRunner()
+{
+ QTC_ASSERT(!m_clangExecutable.isEmpty(), return 0);
+ QTC_ASSERT(!m_clangLogFileDir.isEmpty(), return 0);
+
+ ClangStaticAnalyzerRunner *runner
+ = new ClangStaticAnalyzerRunner(m_clangExecutable, m_clangLogFileDir, 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;
+ handleFinished();
+
+ QString errorMessage;
+ const QList<Diagnostic> diagnostics = LogFileReader::read(logFilePath, &errorMessage);
+ if (!errorMessage.isEmpty())
+ qCDebug(LOG) << "onRunnerFinishedWithSuccess: Error reading log file:" << errorMessage;
+ if (!diagnostics.isEmpty())
+ emit newDiagnosticsAvailable(diagnostics);
+}
+
+void ClangStaticAnalyzerRunControl::onRunnerFinishedWithFailure(const QString &errorMessage,
+ const QString &errorDetails)
+{
+ qCDebug(LOG) << "onRunnerFinishedWithFailure:" << errorMessage << errorDetails;
+ handleFinished();
+}
+
+void ClangStaticAnalyzerRunControl::handleFinished()
+{
+ --m_runningProcesses;
+ 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_filesToProcess.size());
+}
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzer
diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.h b/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.h
new file mode 100644
index 0000000000..37148b399d
--- /dev/null
+++ b/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+** This file is part of the Qt Enterprise LicenseChecker Add-on.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+****************************************************************************/
+
+#ifndef CLANGSTATICANALYZERRUNCONTROL_H
+#define CLANGSTATICANALYZERRUNCONTROL_H
+
+#include <analyzerbase/analyzerruncontrol.h>
+#include <cpptools/cppprojects.h>
+
+#include <QFutureInterface>
+#include <QStringList>
+
+namespace ClangStaticAnalyzer {
+namespace Internal {
+
+class ClangStaticAnalyzerRunner;
+class Diagnostic;
+
+class ClangStaticAnalyzerRunControl : public Analyzer::AnalyzerRunControl
+{
+ Q_OBJECT
+
+public:
+ struct FileConfiguration {
+ FileConfiguration(const QString &filePath, const CppTools::ProjectPart::Ptr &projectPart)
+ : filePath(filePath)
+ , projectPart(projectPart) {}
+
+ QString filePath;
+ CppTools::ProjectPart::Ptr projectPart;
+ };
+
+public:
+ explicit ClangStaticAnalyzerRunControl(const Analyzer::AnalyzerStartParameters &startParams,
+ ProjectExplorer::RunConfiguration *runConfiguration);
+
+ bool startEngine();
+ void stopEngine();
+
+signals:
+ void newDiagnosticsAvailable(const QList<Diagnostic> &diagnostics);
+
+private:
+ void analyzeNextFile();
+ ClangStaticAnalyzerRunner *createRunner();
+
+ void onRunnerFinishedWithSuccess(const QString &logFilePath);
+ void onRunnerFinishedWithFailure(const QString &errorMessage, const QString &errorDetails);
+ void handleFinished();
+
+ void onProgressCanceled();
+ void updateProgressValue();
+
+private:
+ QString m_clangExecutable;
+ QString m_clangLogFileDir;
+ QFutureInterface<void> m_progress;
+ QList<FileConfiguration> m_filesToProcess;
+ int m_initialFilesToProcessSize;
+ int m_runningProcesses;
+};
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzer
+
+#endif // CLANGSTATICANALYZERRUNCONTROL_H
diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrolfactory.cpp b/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrolfactory.cpp
new file mode 100644
index 0000000000..dbc165c805
--- /dev/null
+++ b/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrolfactory.cpp
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+** This file is part of the Qt Enterprise LicenseChecker Add-on.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+****************************************************************************/
+
+#include "clangstaticanalyzerruncontrolfactory.h"
+
+#include <analyzerbase/analyzermanager.h>
+#include <analyzerbase/analyzerruncontrol.h>
+#include <analyzerbase/analyzerstartparameters.h>
+
+#include <projectexplorer/localapplicationrunconfiguration.h>
+
+using namespace Analyzer;
+using namespace ProjectExplorer;
+
+namespace ClangStaticAnalyzer {
+namespace Internal {
+
+ClangStaticAnalyzerRunControlFactory::ClangStaticAnalyzerRunControlFactory(QObject *parent)
+ : IRunControlFactory(parent)
+{
+}
+
+bool ClangStaticAnalyzerRunControlFactory::canRun(RunConfiguration *runConfiguration,
+ RunMode runMode) const
+{
+ return runMode == ClangStaticAnalyzerMode
+ && (qobject_cast<LocalApplicationRunConfiguration *>(runConfiguration));
+}
+
+RunControl *ClangStaticAnalyzerRunControlFactory::create(RunConfiguration *runConfiguration,
+ RunMode runMode,
+ QString *errorMessage)
+{
+ Q_UNUSED(errorMessage);
+ Q_UNUSED(runMode);
+
+ auto *rc = qobject_cast<LocalApplicationRunConfiguration *>(runConfiguration);
+ QTC_ASSERT(rc, return 0);
+
+ AnalyzerStartParameters sp;
+ sp.runMode = runMode;
+ sp.startMode = StartLocal;
+ return AnalyzerManager::createRunControl(sp, runConfiguration);
+}
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzer
diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrolfactory.h b/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrolfactory.h
new file mode 100644
index 0000000000..ec3b2d8c9c
--- /dev/null
+++ b/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrolfactory.h
@@ -0,0 +1,45 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+** This file is part of the Qt Enterprise LicenseChecker Add-on.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+****************************************************************************/
+
+#ifndef CLANGSTATICANALYZERRUNCONTROLFACTORY_H
+#define CLANGSTATICANALYZERRUNCONTROLFACTORY_H
+
+#include <projectexplorer/runconfiguration.h>
+
+namespace ClangStaticAnalyzer {
+namespace Internal {
+
+class ClangStaticAnalyzerRunControlFactory : public ProjectExplorer::IRunControlFactory
+{
+ Q_OBJECT
+
+public:
+ explicit ClangStaticAnalyzerRunControlFactory(QObject *parent = 0);
+
+ bool canRun(ProjectExplorer::RunConfiguration *runConfiguration,
+ ProjectExplorer::RunMode runMode) const;
+
+ ProjectExplorer::RunControl *create(ProjectExplorer::RunConfiguration *runConfiguration,
+ ProjectExplorer::RunMode runMode,
+ QString *errorMessage);
+};
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzer
+
+#endif // CLANGSTATICANALYZERRUNCONTROLFACTORY_H
diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerrunner.cpp b/plugins/clangstaticanalyzer/clangstaticanalyzerrunner.cpp
new file mode 100644
index 0000000000..3db69c644b
--- /dev/null
+++ b/plugins/clangstaticanalyzer/clangstaticanalyzerrunner.cpp
@@ -0,0 +1,165 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+** This file is part of the Qt Enterprise LicenseChecker Add-on.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+****************************************************************************/
+
+#include "clangstaticanalyzerrunner.h"
+
+#include "clangstaticanalyzerconstants.h"
+
+#include <QDebug>
+#include <QDir>
+#include <QFileInfo>
+#include <QLoggingCategory>
+#include <QTemporaryFile>
+
+static Q_LOGGING_CATEGORY(LOG, "qt.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 &definesAndIncludes)
+{
+ QStringList arguments = QStringList()
+ << QLatin1String("--analyze")
+ << QLatin1String("-o")
+ << logFile
+ ;
+ arguments += definesAndIncludes;
+ arguments << 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,
+ 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);
+ 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()
+{
+ const QProcess::ProcessState processState = m_process.state();
+ if (processState == QProcess::Starting || processState == QProcess::Running)
+ m_process.kill();
+}
+
+bool ClangStaticAnalyzerRunner::run(const QString &filePath, const QStringList &definesAndIncludes)
+{
+ QTC_ASSERT(!m_clangExecutable.isEmpty(), return false);
+
+ m_processOutput.clear();
+
+ m_logFile = createLogFile(filePath);
+ QTC_ASSERT(!m_logFile.isEmpty(), return false);
+ const QStringList arguments = constructCommandLineArguments(filePath, m_logFile,
+ definesAndIncludes);
+ m_commandLine = m_clangExecutable + QLatin1Char(' ') + arguments.join(QLatin1Char(' '));
+
+ qCDebug(LOG) << "Starting" << m_commandLine;
+ m_process.start(m_clangExecutable, arguments);
+ return true;
+}
+
+void ClangStaticAnalyzerRunner::onProcessStarted()
+{
+ emit started();
+}
+
+void ClangStaticAnalyzerRunner::onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus)
+{
+ if (exitStatus == QProcess::NormalExit) {
+ if (exitCode == 0)
+ emit finishedWithSuccess(m_logFile);
+ 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));
+}
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzer
diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerrunner.h b/plugins/clangstaticanalyzer/clangstaticanalyzerrunner.h
new file mode 100644
index 0000000000..ada1b67d87
--- /dev/null
+++ b/plugins/clangstaticanalyzer/clangstaticanalyzerrunner.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+** This file is part of the Qt Enterprise LicenseChecker Add-on.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+****************************************************************************/
+
+#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,
+ QObject *parent = 0);
+ ~ClangStaticAnalyzerRunner();
+
+ bool run(const QString &filePath, const QStringList &definesAndIncludes = QStringList());
+
+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;
+
+private:
+ QString m_clangExecutable;
+ QString m_clangLogFileDir;
+ QString m_logFile;
+ QString m_commandLine;
+ QProcess m_process;
+ QByteArray m_processOutput;
+};
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzer
+
+#endif // CLANGSTATICANALYZERRUNNER_H
diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzersettings.cpp b/plugins/clangstaticanalyzer/clangstaticanalyzersettings.cpp
new file mode 100644
index 0000000000..a6445b1630
--- /dev/null
+++ b/plugins/clangstaticanalyzer/clangstaticanalyzersettings.cpp
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+** This file is part of the Qt Enterprise LicenseChecker Add-on.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+****************************************************************************/
+
+#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;
+}
+
+QString ClangStaticAnalyzerSettings::clangExecutable() const
+{
+ return m_clangExecutable;
+}
+
+void ClangStaticAnalyzerSettings::setClangExecutable(const QString &exectuable)
+{
+ QTC_ASSERT(!exectuable.isEmpty(), return);
+ 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));
+
+ const QString defaultClangExecutable = Utils::HostOsInfo::withExecutableSuffix(
+ QLatin1String(Constants::CLANG_EXECUTABLE_BASE_NAME));
+ setClangExecutable(settings->value(QLatin1String(clangExecutableKey),
+ defaultClangExecutable).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), clangExecutable());
+ settings->setValue(QLatin1String(simultaneousProcessesKey), simultaneousProcesses());
+ settings->endGroup();
+}
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzer
diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzersettings.h b/plugins/clangstaticanalyzer/clangstaticanalyzersettings.h
new file mode 100644
index 0000000000..bca154c561
--- /dev/null
+++ b/plugins/clangstaticanalyzer/clangstaticanalyzersettings.h
@@ -0,0 +1,54 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+** This file is part of the Qt Enterprise LicenseChecker Add-on.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+****************************************************************************/
+
+#ifndef CLANGSTATICANALYZERSETTINGS_H
+#define CLANGSTATICANALYZERSETTINGS_H
+
+#include <QObject>
+#include <QString>
+
+namespace ClangStaticAnalyzer {
+namespace Internal {
+
+class ClangStaticAnalyzerSettings : public QObject
+{
+ Q_OBJECT
+
+public:
+ static ClangStaticAnalyzerSettings *instance();
+
+ void writeSettings() const;
+
+ QString clangExecutable() 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/plugins/clangstaticanalyzer/clangstaticanalyzertool.cpp b/plugins/clangstaticanalyzer/clangstaticanalyzertool.cpp
new file mode 100644
index 0000000000..143043d73f
--- /dev/null
+++ b/plugins/clangstaticanalyzer/clangstaticanalyzertool.cpp
@@ -0,0 +1,187 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+** This file is part of the Qt Enterprise LicenseChecker Add-on.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+****************************************************************************/
+
+#include "clangstaticanalyzertool.h"
+
+#include "clangstaticanalyzerdiagnosticmodel.h"
+#include "clangstaticanalyzerdiagnosticview.h"
+#include "clangstaticanalyzerruncontrol.h"
+
+#include <analyzerbase/analyzermanager.h>
+#include <coreplugin/coreconstants.h>
+#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/session.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 {
+
+ClangStaticAnalyzerTool::ClangStaticAnalyzerTool(QObject *parent)
+ : IAnalyzerTool(parent)
+ , m_diagnosticModel(0)
+ , m_diagnosticView(0)
+ , m_goBack(0)
+ , m_goNext(0)
+{
+ setObjectName(QLatin1String("ClangStaticAnalyzerTool"));
+ setRunMode(ProjectExplorer::ClangStaticAnalyzerMode);
+ setToolMode(AnyMode);
+}
+
+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 DetailedErrorView;
+ m_diagnosticView->setItemDelegate(new ClangStaticAnalyzerDiagnosticDelegate(m_diagnosticView));
+ m_diagnosticView->setObjectName(QLatin1String("ClangStaticAnalyzerIssuesView"));
+ m_diagnosticView->setFrameStyle(QFrame::NoFrame);
+ m_diagnosticView->setAttribute(Qt::WA_MacShowFocusRect, false);
+ m_diagnosticModel = new ClangStaticAnalyzerDiagnosticModel(m_diagnosticView);
+ // TODO: Make use of the proxy model
+ QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel(m_diagnosticView);
+ proxyModel->setSourceModel(m_diagnosticModel);
+ m_diagnosticView->setModel(proxyModel);
+ m_diagnosticView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
+ m_diagnosticView->setAutoScroll(false);
+ m_diagnosticView->setObjectName(QLatin1String("ClangStaticAnalyzerIssuesView"));
+ m_diagnosticView->setWindowTitle(tr("Clang Static Analyzer Issues"));
+
+ QDockWidget *issuesDock = AnalyzerManager::createDockWidget(this, m_diagnosticView);
+ 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(QIcon(QLatin1String(Core::Constants::ICON_PREV)));
+ 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(QIcon(QLatin1String(Core::Constants::ICON_NEXT)));
+ 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(
+ const AnalyzerStartParameters &sp,
+ ProjectExplorer::RunConfiguration *runConfiguration)
+{
+ ClangStaticAnalyzerRunControl *engine = new ClangStaticAnalyzerRunControl(sp, runConfiguration);
+ connect(engine, &ClangStaticAnalyzerRunControl::starting,
+ this, &ClangStaticAnalyzerTool::onEngineIsStarting);
+ connect(engine, &ClangStaticAnalyzerRunControl::newDiagnosticsAvailable,
+ this, &ClangStaticAnalyzerTool::onNewDiagnosticsAvailable);
+ connect(engine, &ClangStaticAnalyzerRunControl::finished,
+ this, &ClangStaticAnalyzerTool::onEngineFinished);
+ return engine;
+}
+
+void ClangStaticAnalyzerTool::startTool(StartMode mode)
+{
+ QTC_ASSERT(mode == Analyzer::StartLocal, return);
+
+ AnalyzerManager::showMode();
+ if (Project *pro = SessionManager::startupProject())
+ ProjectExplorerPlugin::instance()->runProject(pro, runMode());
+}
+
+void ClangStaticAnalyzerTool::onEngineIsStarting()
+{
+ QTC_ASSERT(m_diagnosticModel, return);
+ m_diagnosticModel->clear();
+ setBusyCursor(true);
+}
+
+void ClangStaticAnalyzerTool::onNewDiagnosticsAvailable(const QList<Diagnostic> &diagnostics)
+{
+ QTC_ASSERT(m_diagnosticModel, return);
+ m_diagnosticModel->addDiagnostics(diagnostics);
+}
+
+void ClangStaticAnalyzerTool::onEngineFinished()
+{
+ QTC_ASSERT(m_goBack, return);
+ QTC_ASSERT(m_goNext, return);
+ QTC_ASSERT(m_diagnosticModel, return);
+ const int issuesFound = m_diagnosticModel->rowCount();
+ m_goBack->setEnabled(issuesFound > 1);
+ m_goNext->setEnabled(issuesFound > 1);
+ setBusyCursor(false);
+
+ AnalyzerManager::showStatusMessage(issuesFound > 0
+ ? AnalyzerManager::tr("Clang Static Analyzer finished, %n issues were found.", 0, issuesFound)
+ : AnalyzerManager::tr("Clang Static Analyzer finished, no issues were found."));
+}
+
+void ClangStaticAnalyzerTool::setBusyCursor(bool busy)
+{
+ QTC_ASSERT(m_diagnosticView, return);
+ QCursor cursor(busy ? Qt::BusyCursor : Qt::ArrowCursor);
+ m_diagnosticView->setCursor(cursor);
+}
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzer
diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzertool.h b/plugins/clangstaticanalyzer/clangstaticanalyzertool.h
new file mode 100644
index 0000000000..2cb1393a7a
--- /dev/null
+++ b/plugins/clangstaticanalyzer/clangstaticanalyzertool.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+** This file is part of the Qt Enterprise LicenseChecker Add-on.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+****************************************************************************/
+
+#ifndef CLANGSTATICANALYZERTOOL_H
+#define CLANGSTATICANALYZERTOOL_H
+
+#include <analyzerbase/ianalyzertool.h>
+
+namespace Analyzer { class DetailedErrorView; }
+
+namespace ClangStaticAnalyzer {
+namespace Internal {
+
+class ClangStaticAnalyzerDiagnosticModel;
+class ClangStaticAnalyzerDiagnosticView;
+class Diagnostic;
+
+class ClangStaticAnalyzerTool : public Analyzer::IAnalyzerTool
+{
+ Q_OBJECT
+
+public:
+ explicit ClangStaticAnalyzerTool(QObject *parent = 0);
+
+private:
+ QWidget *createWidgets();
+ Analyzer::AnalyzerRunControl *createRunControl(const Analyzer::AnalyzerStartParameters &sp,
+ ProjectExplorer::RunConfiguration *runConfiguration);
+ void startTool(Analyzer::StartMode mode);
+
+ void onEngineIsStarting();
+ void onNewDiagnosticsAvailable(const QList<Diagnostic> &diagnostics);
+ void onEngineFinished();
+
+ void setBusyCursor(bool busy);
+
+private:
+ ClangStaticAnalyzerDiagnosticModel *m_diagnosticModel;
+ Analyzer::DetailedErrorView *m_diagnosticView;
+
+ QAction *m_goBack;
+ QAction *m_goNext;
+};
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzer
+
+#endif // CLANGSTATICANALYZERTOOL_H
diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerutils.cpp b/plugins/clangstaticanalyzer/clangstaticanalyzerutils.cpp
new file mode 100644
index 0000000000..6beff9e192
--- /dev/null
+++ b/plugins/clangstaticanalyzer/clangstaticanalyzerutils.cpp
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+** This file is part of the Qt Enterprise LicenseChecker Add-on.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+****************************************************************************/
+
+#include "clangstaticanalyzerutils.h"
+
+#include "clangstaticanalyzerdiagnostic.h"
+#include "clangstaticanalyzersettings.h"
+
+#include <utils/environment.h>
+
+#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(bool *isValid)
+{
+ return clangExecutable(ClangStaticAnalyzerSettings::instance()->clangExecutable(), 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);
+ return executable;
+}
+
+QString createFullLocationString(const ClangStaticAnalyzer::Internal::Location &location)
+{
+ const QString filePath = location.filePath;
+ const QString lineNumber = QString::number(location.line);
+ return filePath + QLatin1Char(':') + lineNumber;
+}
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzer
diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerutils.h b/plugins/clangstaticanalyzer/clangstaticanalyzerutils.h
new file mode 100644
index 0000000000..8c63470022
--- /dev/null
+++ b/plugins/clangstaticanalyzer/clangstaticanalyzerutils.h
@@ -0,0 +1,43 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+** This file is part of the Qt Enterprise Qt LicenseChecker Add-on.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+****************************************************************************/
+
+#ifndef CLANGSTATICANALYZERUTILS_H
+#define CLANGSTATICANALYZERUTILS_H
+
+#include <QtGlobal>
+
+QT_BEGIN_NAMESPACE
+class QString;
+QT_END_NAMESPACE
+
+
+
+namespace ClangStaticAnalyzer {
+namespace Internal {
+
+class Location;
+
+QString clangExecutable(const QString &fileNameOrPath, bool *isValid);
+QString clangExecutableFromSettings(bool *isValid);
+
+QString createFullLocationString(const ClangStaticAnalyzer::Internal::Location &location);
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzer
+
+#endif // CLANGSTATICANALYZERUTILS_H
diff --git a/plugins/clangstaticanalyzer/tests/clangstaticanalyzerlogfilereader/clangstaticanalyzerlogfilereader.pro b/plugins/clangstaticanalyzer/tests/clangstaticanalyzerlogfilereader/clangstaticanalyzerlogfilereader.pro
new file mode 100644
index 0000000000..d9dda64f85
--- /dev/null
+++ b/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/plugins/clangstaticanalyzer/tests/clangstaticanalyzerlogfilereader/data/noDiagnostics.plist b/plugins/clangstaticanalyzer/tests/clangstaticanalyzerlogfilereader/data/noDiagnostics.plist
new file mode 100644
index 0000000000..2cccbf770f
--- /dev/null
+++ b/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/plugins/clangstaticanalyzer/tests/clangstaticanalyzerlogfilereader/data/someDiagnostics.plist b/plugins/clangstaticanalyzer/tests/clangstaticanalyzerlogfilereader/data/someDiagnostics.plist
new file mode 100644
index 0000000000..3fa5effad2
--- /dev/null
+++ b/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/plugins/clangstaticanalyzer/tests/clangstaticanalyzerlogfilereader/tst_clangstaticanalyzerlogfilereader.cpp b/plugins/clangstaticanalyzer/tests/clangstaticanalyzerlogfilereader/tst_clangstaticanalyzerlogfilereader.cpp
new file mode 100644
index 0000000000..76dd0cf862
--- /dev/null
+++ b/plugins/clangstaticanalyzer/tests/clangstaticanalyzerlogfilereader/tst_clangstaticanalyzerlogfilereader.cpp
@@ -0,0 +1,175 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+** This file is part of the Qt Enterprise LicenseChecker Add-on.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+****************************************************************************/
+
+#include <clangstaticanalyzer/clangstaticanalyzerlogfilereader.h>
+
+#include <utils/fileutils.h>
+
+#include <QtTest>
+
+enum { debug = 0 };
+
+namespace ClangStaticAnalyzer {
+namespace Internal {
+
+static bool operator==(const Location &first, const Location &second)
+{
+ return first.filePath == second.filePath
+ && first.line == second.line
+ && first.column == second.column;
+}
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzer
+
+using namespace ClangStaticAnalyzer::Internal;
+
+namespace {
+
+QDebug operator<<(QDebug dbg, const Location &location)
+{
+ dbg.nospace() << "Location(" << location.filePath << ", "
+ << location.line << ", "
+ << location.column << ')';
+ return dbg.space();
+}
+
+QDebug operator<<(QDebug dbg, const ExplainingStep &step)
+{
+ dbg << '\n'
+ << " ExplainingStep\n"
+ << " location:" << step.location << '\n'
+ << " ranges:\n";
+ foreach (const Location &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, Location(commonPath, 36, 3));
+
+ QCOMPARE(d1.explainingSteps.size(), 2);
+ const ExplainingStep step1 = d1.explainingSteps.at(0);
+ QCOMPARE(step1.location, Location(commonPath, 35, 3));
+ QCOMPARE(step1.ranges.size(), 2);
+ QCOMPARE(step1.ranges.at(0), Location(commonPath, 35, 3));
+ QCOMPARE(step1.ranges.at(1), Location(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, Location(commonPath, 36, 3));
+ QCOMPARE(step2.ranges.size(), 2);
+ QCOMPARE(step2.ranges.at(0), Location(commonPath, 36, 3));
+ QCOMPARE(step2.ranges.at(1), Location(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/plugins/clangstaticanalyzer/tests/clangstaticanalyzerrunner/clangstaticanalyzerrunner.pro b/plugins/clangstaticanalyzer/tests/clangstaticanalyzerrunner/clangstaticanalyzerrunner.pro
new file mode 100644
index 0000000000..b19bae4092
--- /dev/null
+++ b/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/plugins/clangstaticanalyzer/tests/clangstaticanalyzerrunner/tst_clangstaticanalyzerrunner.cpp b/plugins/clangstaticanalyzer/tests/clangstaticanalyzerrunner/tst_clangstaticanalyzerrunner.cpp
new file mode 100644
index 0000000000..c3a51062c8
--- /dev/null
+++ b/plugins/clangstaticanalyzer/tests/clangstaticanalyzerrunner/tst_clangstaticanalyzerrunner.cpp
@@ -0,0 +1,167 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+** This file is part of the Qt Enterprise LicenseChecker Add-on.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+****************************************************************************/
+
+#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());
+
+ 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());
+
+ 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/plugins/clangstaticanalyzer/tests/tests.pri b/plugins/clangstaticanalyzer/tests/tests.pri
new file mode 100644
index 0000000000..f7971f2624
--- /dev/null
+++ b/plugins/clangstaticanalyzer/tests/tests.pri
@@ -0,0 +1,21 @@
+QTC_LIB_DEPENDS += utils
+
+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)
+isEmpty(QTC_PLUGIN_DIRS): error(Set QTC_PLUGIN_DIRS environment variable for extra plugins)
+
+include(../../../../qt-creator/qtcreator.pri)
+include(../../../../qt-creator/tests/auto/qttestrpath.pri)
+
+PLUGINDIR=$$PWD/../
+
+QT += testlib
+QT -= gui
+
+CONFIG += console
+CONFIG += testcase
+CONFIG -= app_bundle
+
+TEMPLATE = app
diff --git a/plugins/clangstaticanalyzer/tests/tests.pro b/plugins/clangstaticanalyzer/tests/tests.pro
new file mode 100644
index 0000000000..72e7c586e9
--- /dev/null
+++ b/plugins/clangstaticanalyzer/tests/tests.pro
@@ -0,0 +1,6 @@
+TEMPLATE = subdirs
+CONFIG += ordered
+
+SUBDIRS = \
+ clangstaticanalyzerrunner \
+ clangstaticanalyzerlogfilereader
diff --git a/qtcreatorlibrary.pri b/qtcreatorlibrary.pri
new file mode 100644
index 0000000000..aa11abf700
--- /dev/null
+++ b/qtcreatorlibrary.pri
@@ -0,0 +1,7 @@
+IDE_SOURCE_TREE=$$(QTC_SOURCE)
+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)
+
+include($$IDE_SOURCE_TREE/src/qtcreatorlibrary.pri)
diff --git a/qtcreatorplugin.pri b/qtcreatorplugin.pri
new file mode 100644
index 0000000000..190dbed1c3
--- /dev/null
+++ b/qtcreatorplugin.pri
@@ -0,0 +1,8 @@
+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)
+
+INCLUDEPATH+= $$PWD/plugins
+include($$IDE_SOURCE_TREE/src/qtcreatorplugin.pri)