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