Merge Clang static analyzer plugin

Change-Id: I2c67655ac5703feda9a256d349306bad16deace7
This commit is contained in:
Eike Ziller
2016-01-20 15:03:52 +01:00
64 changed files with 5272 additions and 0 deletions

View 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
}

View 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

View 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: ["**/*"]
}
}

View File

@@ -0,0 +1,10 @@
QTC_PLUGIN_NAME = ClangStaticAnalyzer
QTC_LIB_DEPENDS += \
extensionsystem \
utils
QTC_PLUGIN_DEPENDS += \
analyzerbase \
cpptools
QTC_TEST_DEPENDS += \
qbsprojectmanager \
qmakeprojectmanager

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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>

View 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 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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View 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 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

View File

@@ -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

View File

@@ -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

View 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

View 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

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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.

View File

@@ -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"

View File

@@ -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.

View File

@@ -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>

View File

@@ -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

View 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

View 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 "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

View File

@@ -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

View 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

View 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

View 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

View File

@@ -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

View 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"

View 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

View 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

View File

@@ -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

View File

@@ -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>

View 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

View 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

View File

@@ -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 + "/..")
}

View File

@@ -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

View File

@@ -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"
]
}

View File

@@ -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>

View File

@@ -0,0 +1,162 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>clang_version</key>
<string>Ubuntu clang version 3.5-1ubuntu1 (trunk) (based on LLVM 3.5)</string>
<key>files</key>
<array>
<string>../csatestproject/core.CallAndMessage3.cpp</string>
</array>
<key>diagnostics</key>
<array>
<dict>
<key>path</key>
<array>
<dict>
<key>kind</key><string>control</string>
<key>edges</key>
<array>
<dict>
<key>start</key>
<array>
<dict>
<key>line</key><integer>34</integer>
<key>col</key><integer>3</integer>
<key>file</key><integer>0</integer>
</dict>
<dict>
<key>line</key><integer>34</integer>
<key>col</key><integer>6</integer>
<key>file</key><integer>0</integer>
</dict>
</array>
<key>end</key>
<array>
<dict>
<key>line</key><integer>35</integer>
<key>col</key><integer>3</integer>
<key>file</key><integer>0</integer>
</dict>
<dict>
<key>line</key><integer>35</integer>
<key>col</key><integer>5</integer>
<key>file</key><integer>0</integer>
</dict>
</array>
</dict>
</array>
</dict>
<dict>
<key>kind</key><string>event</string>
<key>location</key>
<dict>
<key>line</key><integer>35</integer>
<key>col</key><integer>3</integer>
<key>file</key><integer>0</integer>
</dict>
<key>ranges</key>
<array>
<array>
<dict>
<key>line</key><integer>35</integer>
<key>col</key><integer>3</integer>
<key>file</key><integer>0</integer>
</dict>
<dict>
<key>line</key><integer>35</integer>
<key>col</key><integer>9</integer>
<key>file</key><integer>0</integer>
</dict>
</array>
</array>
<key>depth</key><integer>0</integer>
<key>extended_message</key>
<string>Null pointer value stored to &apos;foo&apos;</string>
<key>message</key>
<string>Null pointer value stored to &apos;foo&apos;</string>
</dict>
<dict>
<key>kind</key><string>control</string>
<key>edges</key>
<array>
<dict>
<key>start</key>
<array>
<dict>
<key>line</key><integer>35</integer>
<key>col</key><integer>3</integer>
<key>file</key><integer>0</integer>
</dict>
<dict>
<key>line</key><integer>35</integer>
<key>col</key><integer>5</integer>
<key>file</key><integer>0</integer>
</dict>
</array>
<key>end</key>
<array>
<dict>
<key>line</key><integer>36</integer>
<key>col</key><integer>3</integer>
<key>file</key><integer>0</integer>
</dict>
<dict>
<key>line</key><integer>36</integer>
<key>col</key><integer>5</integer>
<key>file</key><integer>0</integer>
</dict>
</array>
</dict>
</array>
</dict>
<dict>
<key>kind</key><string>event</string>
<key>location</key>
<dict>
<key>line</key><integer>36</integer>
<key>col</key><integer>3</integer>
<key>file</key><integer>0</integer>
</dict>
<key>ranges</key>
<array>
<array>
<dict>
<key>line</key><integer>36</integer>
<key>col</key><integer>3</integer>
<key>file</key><integer>0</integer>
</dict>
<dict>
<key>line</key><integer>36</integer>
<key>col</key><integer>5</integer>
<key>file</key><integer>0</integer>
</dict>
</array>
</array>
<key>depth</key><integer>0</integer>
<key>extended_message</key>
<string>Called function pointer is null (null dereference)</string>
<key>message</key>
<string>Called function pointer is null (null dereference)</string>
</dict>
</array>
<key>description</key><string>Called function pointer is null (null dereference)</string>
<key>category</key><string>Logic error</string>
<key>type</key><string>Called function pointer is null (null dereference)</string>
<key>issue_context_kind</key><string>function</string>
<key>issue_context</key><string>test</string>
<key>issue_hash</key><string>3</string>
<key>location</key>
<dict>
<key>line</key><integer>36</integer>
<key>col</key><integer>3</integer>
<key>file</key><integer>0</integer>
</dict>
<key>HTMLDiagnostics_files</key>
<array>
<string>report-6c3c2a.html</string>
</array>
</dict>
</array>
</dict>
</plist>

View File

@@ -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"

View File

@@ -0,0 +1,11 @@
include(../tests.pri)
TARGET = tst_clangstaticanalyzerrunnertest
DEFINES += SRCDIR=\\\"$$PWD/\\\"
SOURCES += \
tst_clangstaticanalyzerrunner.cpp \
$$PLUGINDIR/clangstaticanalyzerrunner.cpp
HEADERS += \
$$PLUGINDIR/clangstaticanalyzerrunner.h

View File

@@ -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",
]
}

View File

@@ -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"

View 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

View File

@@ -0,0 +1,6 @@
TEMPLATE = subdirs
CONFIG += ordered
SUBDIRS = \
clangstaticanalyzerrunner \
clangstaticanalyzerlogfilereader

View File

@@ -0,0 +1,8 @@
import qbs
Project {
references: [
"clangstaticanalyzerlogfilereader",
"clangstaticanalyzerrunner",
]
}

View File

@@ -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();
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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>

View File

@@ -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

View File

@@ -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"
]
}

View File

@@ -0,0 +1,5 @@
int main()
{
int *i = 0;
*i = 42;
}

View File

@@ -0,0 +1,3 @@
CONFIG -= QT
SOURCES = main.cpp

View File

@@ -0,0 +1,5 @@
import qbs
CppApplication {
files: ["main.cpp"]
}

View File

@@ -3,6 +3,7 @@ include(../../qtcreator.pri)
TEMPLATE = subdirs
SUBDIRS = \
clangstaticanalyzer \
coreplugin \
texteditor \
cppeditor \

View File

@@ -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",