forked from qt-creator/qt-creator
ClangTools: Add tool that runs clang-tidy and clazy
... over the whole project. Generate and read serialized files to get diagnostics. Change-Id: Iafc25fc70443107a040a995efc038aed35102bbf Reviewed-by: Nikolai Kosjar <nikolai.kosjar@qt.io>
This commit is contained in:
@@ -528,17 +528,12 @@ private:
|
||||
if (tidyMode == Mode::Disabled)
|
||||
return;
|
||||
|
||||
QString checks;
|
||||
if (tidyMode == Mode::ChecksPrefixList) {
|
||||
checks = QStringLiteral("-*") + diagnosticConfig.clangTidyChecksPrefixes();
|
||||
} else if (tidyMode == Mode::ChecksString) {
|
||||
checks = diagnosticConfig.clangTidyChecksString();
|
||||
checks = checks.simplified();
|
||||
checks.replace(" ", "");
|
||||
}
|
||||
|
||||
addXclangArg("-add-plugin", "clang-tidy");
|
||||
|
||||
if (tidyMode == Mode::File)
|
||||
return;
|
||||
|
||||
const QString checks = diagnosticConfig.clangTidyChecks();
|
||||
if (!checks.isEmpty())
|
||||
addXclangArg("-plugin-arg-clang-tidy", "-checks=" + checks);
|
||||
}
|
||||
|
@@ -226,10 +226,11 @@ void ClangStaticAnalyzerTool::handleStateUpdate()
|
||||
Debugger::showPermanentStatusMessage(message);
|
||||
}
|
||||
|
||||
QList<Diagnostic> ClangStaticAnalyzerTool::read(const QString &filePath,
|
||||
QList<Diagnostic> ClangStaticAnalyzerTool::read(const QString &,
|
||||
const QString &logFilePath,
|
||||
QString *errorMessage) const
|
||||
{
|
||||
return LogFileReader::readPlist(filePath, errorMessage);
|
||||
return LogFileReader::readPlist(logFilePath, errorMessage);
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
|
@@ -49,7 +49,8 @@ public:
|
||||
|
||||
void startTool() final;
|
||||
|
||||
QList<Diagnostic> read(const QString &filePath,
|
||||
QList<Diagnostic> read(const QString &,
|
||||
const QString &logFilePath,
|
||||
QString *errorMessage) const final;
|
||||
|
||||
private:
|
||||
|
66
src/plugins/clangtools/clangtidyclazyruncontrol.cpp
Normal file
66
src/plugins/clangtools/clangtidyclazyruncontrol.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 "clangtidyclazyruncontrol.h"
|
||||
|
||||
#include "clangtidyclazyrunner.h"
|
||||
#include "clangtidyclazytool.h"
|
||||
|
||||
using namespace ProjectExplorer;
|
||||
|
||||
namespace ClangTools {
|
||||
namespace Internal {
|
||||
|
||||
ClangTidyClazyRunControl::ClangTidyClazyRunControl(RunControl *runControl, Target *target)
|
||||
: ClangToolRunControl(runControl, target)
|
||||
{
|
||||
setDisplayName("ClangTidyClazyRunner");
|
||||
init();
|
||||
}
|
||||
|
||||
ClangToolRunner *ClangTidyClazyRunControl::createRunner()
|
||||
{
|
||||
QTC_ASSERT(!m_clangExecutable.isEmpty(), return 0);
|
||||
QTC_ASSERT(!m_clangLogFileDir.isEmpty(), return 0);
|
||||
|
||||
auto runner = new ClangTidyClazyRunner(m_clangExecutable,
|
||||
m_clangLogFileDir,
|
||||
m_environment,
|
||||
this);
|
||||
connect(runner, &ClangTidyClazyRunner::finishedWithSuccess,
|
||||
this, &ClangTidyClazyRunControl::onRunnerFinishedWithSuccess);
|
||||
connect(runner, &ClangTidyClazyRunner::finishedWithFailure,
|
||||
this, &ClangTidyClazyRunControl::onRunnerFinishedWithFailure);
|
||||
return runner;
|
||||
}
|
||||
|
||||
ClangTool *ClangTidyClazyRunControl::tool()
|
||||
{
|
||||
return ClangTidyClazyTool::instance();
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace ClangTools
|
||||
|
47
src/plugins/clangtools/clangtidyclazyruncontrol.h
Normal file
47
src/plugins/clangtools/clangtidyclazyruncontrol.h
Normal 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.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "clangtoolruncontrol.h"
|
||||
|
||||
namespace ClangTools {
|
||||
namespace Internal {
|
||||
|
||||
class ClangTidyClazyRunControl final : public ClangToolRunControl
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ClangTidyClazyRunControl(ProjectExplorer::RunControl *runControl,
|
||||
ProjectExplorer::Target *target);
|
||||
|
||||
protected:
|
||||
ClangToolRunner *createRunner() final;
|
||||
ClangTool *tool() final;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace ClangTools
|
101
src/plugins/clangtools/clangtidyclazyrunner.cpp
Normal file
101
src/plugins/clangtools/clangtidyclazyrunner.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "clangtidyclazyrunner.h"
|
||||
|
||||
#include <cpptools/cppcodemodelsettings.h>
|
||||
#include <cpptools/cpptoolsreuse.h>
|
||||
|
||||
#include <utils/synchronousprocess.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
static Q_LOGGING_CATEGORY(LOG, "qtc.clangtools.runner")
|
||||
|
||||
namespace ClangTools {
|
||||
namespace Internal {
|
||||
|
||||
ClangTidyClazyRunner::ClangTidyClazyRunner(const QString &clangExecutable,
|
||||
const QString &clangLogFileDir,
|
||||
const Utils::Environment &environment,
|
||||
QObject *parent)
|
||||
: ClangToolRunner(clangExecutable,
|
||||
clangLogFileDir,
|
||||
environment,
|
||||
tr("Clang-Tidy and Clazy"),
|
||||
parent)
|
||||
{
|
||||
}
|
||||
|
||||
static void addXclangArg(QStringList &arguments,
|
||||
const QString &argName,
|
||||
const QString &argValue = QString())
|
||||
{
|
||||
arguments << QString("-Xclang")
|
||||
<< argName;
|
||||
if (!argValue.isEmpty()) {
|
||||
arguments << QString("-Xclang")
|
||||
<< argValue;
|
||||
}
|
||||
}
|
||||
|
||||
QStringList ClangTidyClazyRunner::constructCommandLineArguments(const QStringList &options)
|
||||
{
|
||||
using namespace CppTools;
|
||||
QStringList arguments;
|
||||
|
||||
if (LOG().isDebugEnabled())
|
||||
arguments << QString("-v");
|
||||
|
||||
arguments << QString("-fsyntax-only")
|
||||
<< QString("-serialize-diagnostics")
|
||||
<< QString(m_logFile);
|
||||
|
||||
const ClangDiagnosticConfig config = CppTools::codeModelSettings()->clangDiagnosticConfig();
|
||||
|
||||
const ClangDiagnosticConfig::TidyMode tidyMode = config.clangTidyMode();
|
||||
if (tidyMode != ClangDiagnosticConfig::TidyMode::Disabled) {
|
||||
addXclangArg(arguments, QString("-add-plugin"), QString("clang-tidy"));
|
||||
if (tidyMode != ClangDiagnosticConfig::TidyMode::File) {
|
||||
const QString tidyChecks = config.clangTidyChecks();
|
||||
addXclangArg(arguments, QString("-plugin-arg-clang-tidy"), "-checks=" + tidyChecks);
|
||||
}
|
||||
}
|
||||
|
||||
const QString clazyChecks = config.clazyChecks();
|
||||
if (!clazyChecks.isEmpty()) {
|
||||
addXclangArg(arguments, QString("-add-plugin"), QString("clang-lazy"));
|
||||
addXclangArg(arguments, QString("-plugin-arg-clang-lazy"), config.clazyChecks());
|
||||
}
|
||||
|
||||
arguments += options;
|
||||
arguments << QDir::toNativeSeparators(filePath());
|
||||
return arguments;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace ClangTools
|
47
src/plugins/clangtools/clangtidyclazyrunner.h
Normal file
47
src/plugins/clangtools/clangtidyclazyrunner.h
Normal 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.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "clangtoolrunner.h"
|
||||
|
||||
namespace ClangTools {
|
||||
namespace Internal {
|
||||
|
||||
class ClangTidyClazyRunner final : public ClangToolRunner
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ClangTidyClazyRunner(const QString &clangExecutable,
|
||||
const QString &clangLogFileDir,
|
||||
const Utils::Environment &environment,
|
||||
QObject *parent = nullptr);
|
||||
protected:
|
||||
QStringList constructCommandLineArguments(const QStringList &options) final;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace ClangTools
|
196
src/plugins/clangtools/clangtidyclazytool.cpp
Normal file
196
src/plugins/clangtools/clangtidyclazytool.cpp
Normal file
@@ -0,0 +1,196 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 "clangtidyclazytool.h"
|
||||
|
||||
#include "clangtoolsconstants.h"
|
||||
#include "clangtoolsdiagnosticmodel.h"
|
||||
#include "clangtoolslogfilereader.h"
|
||||
#include "clangtidyclazyruncontrol.h"
|
||||
|
||||
#include <coreplugin/actionmanager/actioncontainer.h>
|
||||
#include <coreplugin/actionmanager/actionmanager.h>
|
||||
|
||||
#include <debugger/analyzer/analyzermanager.h>
|
||||
|
||||
#include <projectexplorer/kitinformation.h>
|
||||
#include <projectexplorer/projectexplorer.h>
|
||||
#include <projectexplorer/projectexplorericons.h>
|
||||
#include <projectexplorer/target.h>
|
||||
#include <projectexplorer/session.h>
|
||||
|
||||
#include <utils/utilsicons.h>
|
||||
|
||||
#include <QAction>
|
||||
|
||||
using namespace Core;
|
||||
using namespace Debugger;
|
||||
using namespace ProjectExplorer;
|
||||
using namespace Utils;
|
||||
|
||||
namespace ClangTools {
|
||||
namespace Internal {
|
||||
|
||||
static ClangTidyClazyTool *s_instance;
|
||||
|
||||
ClangTidyClazyTool::ClangTidyClazyTool()
|
||||
: ClangTool("Clang-Tidy and Clazy")
|
||||
{
|
||||
setObjectName("ClangTidyClazyTool");
|
||||
s_instance = this;
|
||||
|
||||
m_diagnosticView = new Debugger::DetailedErrorView;
|
||||
initDiagnosticView();
|
||||
m_diagnosticView->setModel(m_diagnosticModel);
|
||||
m_diagnosticView->setObjectName(QLatin1String("ClangTidyClazyIssuesView"));
|
||||
m_diagnosticView->setWindowTitle(tr("Clang-Tidy and Clazy Issues"));
|
||||
|
||||
ActionContainer *menu = ActionManager::actionContainer(Debugger::Constants::M_DEBUG_ANALYZER);
|
||||
const QString toolTip = tr("Clang-Tidy and Clazy use a customized Clang executable from the "
|
||||
"Clang project to search for errors and warnings.");
|
||||
|
||||
Debugger::registerPerspective(ClangTidyClazyPerspectiveId, new Perspective(
|
||||
tr("Clang-Tidy and Clazy"),
|
||||
{{ClangTidyClazyDockId, m_diagnosticView, {}, Perspective::SplitVertical}}
|
||||
));
|
||||
|
||||
auto *action = new QAction(tr("Clang-Tidy and Clazy"), this);
|
||||
action->setToolTip(toolTip);
|
||||
menu->addAction(ActionManager::registerAction(action, "ClangTidyClazy.Action"),
|
||||
Debugger::Constants::G_ANALYZER_TOOLS);
|
||||
QObject::connect(action, &QAction::triggered, this, &ClangTidyClazyTool::startTool);
|
||||
QObject::connect(m_startAction, &QAction::triggered, action, &QAction::triggered);
|
||||
QObject::connect(m_startAction, &QAction::changed, action, [action, this] {
|
||||
action->setEnabled(m_startAction->isEnabled());
|
||||
});
|
||||
|
||||
ToolbarDescription tidyClazyToolbar;
|
||||
tidyClazyToolbar.addAction(m_startAction);
|
||||
tidyClazyToolbar.addAction(m_stopAction);
|
||||
Debugger::registerToolbar(ClangTidyClazyPerspectiveId, tidyClazyToolbar);
|
||||
|
||||
updateRunActions();
|
||||
|
||||
connect(ProjectExplorerPlugin::instance(), &ProjectExplorerPlugin::updateRunActions,
|
||||
this, &ClangTidyClazyTool::updateRunActions);
|
||||
|
||||
}
|
||||
|
||||
ClangTidyClazyTool *ClangTidyClazyTool::instance()
|
||||
{
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
void ClangTidyClazyTool::startTool()
|
||||
{
|
||||
auto runControl = new RunControl(nullptr, Constants::CLANGTIDYCLAZY_RUN_MODE);
|
||||
runControl->setDisplayName(tr("Clang-Tidy and Clazy"));
|
||||
runControl->setIcon(ProjectExplorer::Icons::ANALYZER_START_SMALL_TOOLBAR);
|
||||
|
||||
Project *project = SessionManager::startupProject();
|
||||
QTC_ASSERT(project, return);
|
||||
|
||||
auto clangTool = new ClangTidyClazyRunControl(runControl, project->activeTarget());
|
||||
|
||||
m_stopAction->disconnect();
|
||||
connect(m_stopAction, &QAction::triggered, runControl, [runControl] {
|
||||
runControl->appendMessage(tr("Clang-Tidy and Clazy tool stopped by user."),
|
||||
NormalMessageFormat);
|
||||
runControl->initiateStop();
|
||||
});
|
||||
|
||||
connect(runControl, &RunControl::stopped, this, [this, clangTool] {
|
||||
bool success = clangTool->success();
|
||||
setToolBusy(false);
|
||||
m_running = false;
|
||||
handleStateUpdate();
|
||||
updateRunActions();
|
||||
emit finished(success);
|
||||
});
|
||||
|
||||
Debugger::selectPerspective(ClangTidyClazyPerspectiveId);
|
||||
|
||||
m_diagnosticModel->clear();
|
||||
setToolBusy(true);
|
||||
m_running = true;
|
||||
handleStateUpdate();
|
||||
updateRunActions();
|
||||
|
||||
ProjectExplorerPlugin::startRunControl(runControl);
|
||||
}
|
||||
|
||||
void ClangTidyClazyTool::updateRunActions()
|
||||
{
|
||||
if (m_toolBusy) {
|
||||
m_startAction->setEnabled(false);
|
||||
QString tooltipText = tr("Clang-Tidy and Clazy are still running.");
|
||||
m_startAction->setToolTip(tooltipText);
|
||||
m_stopAction->setEnabled(true);
|
||||
} else {
|
||||
QString toolTip = tr("Start Clang-Tidy and Clazy.");
|
||||
Project *project = SessionManager::startupProject();
|
||||
Target *target = project ? project->activeTarget() : nullptr;
|
||||
const Core::Id cxx = ProjectExplorer::Constants::CXX_LANGUAGE_ID;
|
||||
bool canRun = target && project->projectLanguages().contains(cxx)
|
||||
&& ToolChainKitInformation::toolChain(target->kit(), cxx);
|
||||
if (!canRun)
|
||||
toolTip = tr("This is not a C++ project.");
|
||||
|
||||
m_startAction->setToolTip(toolTip);
|
||||
m_startAction->setEnabled(canRun);
|
||||
m_stopAction->setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
void ClangTidyClazyTool::handleStateUpdate()
|
||||
{
|
||||
QTC_ASSERT(m_diagnosticModel, return);
|
||||
|
||||
const int issuesFound = m_diagnosticModel->diagnostics().count();
|
||||
QString message;
|
||||
if (m_running)
|
||||
message = tr("Clang-Tidy and Clazy are running.");
|
||||
else
|
||||
message = tr("Clang-Tidy and Clazy finished.");
|
||||
|
||||
message += QLatin1Char(' ');
|
||||
if (issuesFound == 0)
|
||||
message += tr("No issues found.");
|
||||
else
|
||||
message += tr("%n issues found.", nullptr, issuesFound);
|
||||
|
||||
Debugger::showPermanentStatusMessage(message);
|
||||
}
|
||||
|
||||
QList<Diagnostic> ClangTidyClazyTool::read(const QString &filePath,
|
||||
const QString &logFilePath,
|
||||
QString *errorMessage) const
|
||||
{
|
||||
return LogFileReader::readSerialized(filePath, logFilePath, errorMessage);
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace ClangTools
|
||||
|
58
src/plugins/clangtools/clangtidyclazytool.h
Normal file
58
src/plugins/clangtools/clangtidyclazytool.h
Normal 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.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "clangtool.h"
|
||||
|
||||
namespace ClangTools {
|
||||
namespace Internal {
|
||||
|
||||
const char ClangTidyClazyPerspectiveId[] = "ClangTidyClazy.Perspective";
|
||||
const char ClangTidyClazyDockId[] = "ClangTidyClazy.Dock";
|
||||
|
||||
class ClangTidyClazyTool final : public ClangTool
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ClangTidyClazyTool();
|
||||
|
||||
static ClangTidyClazyTool *instance();
|
||||
|
||||
void startTool() final;
|
||||
|
||||
QList<Diagnostic> read(const QString &filePath,
|
||||
const QString &logFilePath,
|
||||
QString *errorMessage) const final;
|
||||
|
||||
private:
|
||||
void handleStateUpdate() final;
|
||||
|
||||
void updateRunActions();
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace ClangTools
|
@@ -47,6 +47,7 @@ public:
|
||||
virtual void startTool() = 0;
|
||||
|
||||
virtual QList<Diagnostic> read(const QString &filePath,
|
||||
const QString &logFilePath,
|
||||
QString *errorMessage) const = 0;
|
||||
|
||||
// For testing.
|
||||
|
@@ -351,12 +351,13 @@ void ClangToolRunControl::analyzeNextFile()
|
||||
Utils::StdOutFormat);
|
||||
}
|
||||
|
||||
void ClangToolRunControl::onRunnerFinishedWithSuccess(const QString &logFilePath)
|
||||
void ClangToolRunControl::onRunnerFinishedWithSuccess(const QString &filePath,
|
||||
const QString &logFilePath)
|
||||
{
|
||||
qCDebug(LOG) << "onRunnerFinishedWithSuccess:" << logFilePath;
|
||||
|
||||
QString errorMessage;
|
||||
const QList<Diagnostic> diagnostics = tool()->read(logFilePath, &errorMessage);
|
||||
const QList<Diagnostic> diagnostics = tool()->read(filePath, logFilePath, &errorMessage);
|
||||
if (!errorMessage.isEmpty()) {
|
||||
qCDebug(LOG) << "onRunnerFinishedWithSuccess: Error reading log file:" << errorMessage;
|
||||
const QString filePath = qobject_cast<ClangToolRunner *>(sender())->filePath();
|
||||
|
@@ -73,7 +73,7 @@ protected:
|
||||
|
||||
virtual ClangToolRunner *createRunner() = 0;
|
||||
|
||||
void onRunnerFinishedWithSuccess(const QString &logFilePath);
|
||||
void onRunnerFinishedWithSuccess(const QString &filePath, const QString &logFilePath);
|
||||
void onRunnerFinishedWithFailure(const QString &errorMessage, const QString &errorDetails);
|
||||
|
||||
private:
|
||||
|
@@ -121,7 +121,7 @@ void ClangToolRunner::onProcessFinished(int exitCode, QProcess::ExitStatus exitS
|
||||
if (exitCode == 0) {
|
||||
qCDebug(LOG).noquote() << "Output:\n" << Utils::SynchronousProcess::normalizeNewlines(
|
||||
QString::fromLocal8Bit(m_processOutput));
|
||||
emit finishedWithSuccess(actualLogFile());
|
||||
emit finishedWithSuccess(m_filePath, actualLogFile());
|
||||
}
|
||||
else
|
||||
emit finishedWithFailure(finishedWithBadExitCode(m_name, exitCode), processCommandlineAndOutput());
|
||||
|
@@ -56,7 +56,7 @@ public:
|
||||
|
||||
signals:
|
||||
void started();
|
||||
void finishedWithSuccess(const QString &logFilePath);
|
||||
void finishedWithSuccess(const QString &filePath, const QString &logFilePath);
|
||||
void finishedWithFailure(const QString &errorMessage, const QString &errorDetails);
|
||||
|
||||
protected:
|
||||
|
@@ -1,4 +1,8 @@
|
||||
include(../../qtcreatorplugin.pri)
|
||||
include(../../shared/clang/clang_installation.pri)
|
||||
|
||||
LIBS += $$LIBCLANG_LIBS
|
||||
INCLUDEPATH += $$LLVM_INCLUDEPATH
|
||||
|
||||
SOURCES += \
|
||||
clangstaticanalyzerconfigwidget.cpp \
|
||||
@@ -9,6 +13,9 @@ SOURCES += \
|
||||
clangstaticanalyzerruncontrol.cpp \
|
||||
clangstaticanalyzerrunner.cpp \
|
||||
clangstaticanalyzertool.cpp \
|
||||
clangtidyclazyruncontrol.cpp \
|
||||
clangtidyclazyrunner.cpp \
|
||||
clangtidyclazytool.cpp \
|
||||
clangtool.cpp \
|
||||
clangtoolruncontrol.cpp \
|
||||
clangtoolrunner.cpp \
|
||||
@@ -28,6 +35,9 @@ HEADERS += \
|
||||
clangstaticanalyzerruncontrol.h \
|
||||
clangstaticanalyzerrunner.h \
|
||||
clangstaticanalyzertool.h \
|
||||
clangtidyclazyruncontrol.h \
|
||||
clangtidyclazyrunner.h \
|
||||
clangtidyclazytool.h \
|
||||
clangtool.h \
|
||||
clangtoolruncontrol.h \
|
||||
clangtoolrunner.h \
|
||||
|
@@ -10,6 +10,7 @@ QtcPlugin {
|
||||
Depends { name: "ProjectExplorer" }
|
||||
Depends { name: "QtcSsh" }
|
||||
Depends { name: "Utils" }
|
||||
Depends { name: "libclang"; required: false }
|
||||
|
||||
Depends { name: "Qt.widgets" }
|
||||
|
||||
@@ -18,6 +19,13 @@ QtcPlugin {
|
||||
"QmakeProjectManager",
|
||||
]
|
||||
|
||||
condition: libclang.present
|
||||
|
||||
cpp.includePaths: base.concat(libclang.llvmIncludeDir)
|
||||
cpp.libraryPaths: base.concat(libclang.llvmLibDir)
|
||||
cpp.dynamicLibraries: base.concat(libclang.llvmLibs)
|
||||
cpp.rpaths: base.concat(libclang.llvmLibDir)
|
||||
|
||||
files: [
|
||||
"clangstaticanalyzerconfigwidget.cpp",
|
||||
"clangstaticanalyzerconfigwidget.h",
|
||||
@@ -37,6 +45,12 @@ QtcPlugin {
|
||||
"clangstaticanalyzerrunner.h",
|
||||
"clangstaticanalyzertool.cpp",
|
||||
"clangstaticanalyzertool.h",
|
||||
"clangtidyclazyruncontrol.cpp",
|
||||
"clangtidyclazyruncontrol.h",
|
||||
"clangtidyclazyrunner.cpp",
|
||||
"clangtidyclazyrunner.h",
|
||||
"clangtidyclazytool.cpp",
|
||||
"clangtidyclazytool.h",
|
||||
"clangtool.cpp",
|
||||
"clangtool.h",
|
||||
"clangtoolruncontrol.cpp",
|
||||
|
@@ -298,6 +298,8 @@ QVariant ExplainingStepItem::data(int column, int role) const
|
||||
}
|
||||
case Qt::ToolTipRole:
|
||||
return createExplainingStepToolTipString(m_step);
|
||||
case Qt::DecorationRole:
|
||||
return (m_step.message.startsWith("fix-it:")) ? iconData("fix-it") : QVariant();
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
|
@@ -33,8 +33,13 @@
|
||||
#include <QRegularExpression>
|
||||
#include <QXmlStreamReader>
|
||||
|
||||
#include <utils/executeondestruction.h>
|
||||
#include <utils/fileutils.h>
|
||||
#include <utils/hostosinfo.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <clang-c/Index.h>
|
||||
|
||||
namespace ClangTools {
|
||||
namespace Internal {
|
||||
|
||||
@@ -74,6 +79,12 @@ private:
|
||||
QList<Diagnostic> m_diagnostics;
|
||||
};
|
||||
|
||||
class ClangSerializedDiagnosticsReader
|
||||
{
|
||||
public:
|
||||
QList<Diagnostic> read(const QString &filePath, const QString &logFilePath);
|
||||
};
|
||||
|
||||
static bool checkFilePath(const QString &filePath, QString *errorMessage)
|
||||
{
|
||||
QFileInfo fi(filePath);
|
||||
@@ -133,6 +144,16 @@ QList<Diagnostic> LogFileReader::readPlist(const QString &filePath, QString *err
|
||||
}
|
||||
}
|
||||
|
||||
QList<Diagnostic> LogFileReader::readSerialized(const QString &filePath, const QString &logFilePath,
|
||||
QString *errorMessage)
|
||||
{
|
||||
if (!checkFilePath(filePath, errorMessage))
|
||||
return QList<Diagnostic>();
|
||||
|
||||
ClangSerializedDiagnosticsReader reader;
|
||||
return reader.read(filePath, logFilePath);
|
||||
}
|
||||
|
||||
ClangStaticAnalyzerLogFileReader::ClangStaticAnalyzerLogFileReader(const QString &filePath)
|
||||
: m_filePath(filePath)
|
||||
{
|
||||
@@ -390,5 +411,157 @@ int ClangStaticAnalyzerLogFileReader::readInteger(bool *convertedSuccessfully)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static QString fromCXString(CXString &&cxString)
|
||||
{
|
||||
QString result = QString::fromUtf8(clang_getCString(cxString));
|
||||
clang_disposeString(cxString);
|
||||
return result;
|
||||
}
|
||||
|
||||
static Debugger::DiagnosticLocation diagLocationFromSourceLocation(CXSourceLocation cxLocation)
|
||||
{
|
||||
CXFile file;
|
||||
unsigned line;
|
||||
unsigned column;
|
||||
clang_getSpellingLocation(cxLocation, &file, &line, &column, nullptr);
|
||||
|
||||
Debugger::DiagnosticLocation location;
|
||||
location.filePath = fromCXString(clang_getFileName(file));
|
||||
location.line = line;
|
||||
location.column = column;
|
||||
return location;
|
||||
}
|
||||
|
||||
static QString cxDiagnosticType(const CXDiagnostic cxDiagnostic)
|
||||
{
|
||||
const CXDiagnosticSeverity severity = clang_getDiagnosticSeverity(cxDiagnostic);
|
||||
switch (severity) {
|
||||
case CXDiagnostic_Note:
|
||||
return QString("note");
|
||||
case CXDiagnostic_Warning:
|
||||
return QString("warning");
|
||||
case CXDiagnostic_Error:
|
||||
return QString("error");
|
||||
case CXDiagnostic_Fatal:
|
||||
return QString("fatal");
|
||||
case CXDiagnostic_Ignored:
|
||||
return QString("ignored");
|
||||
}
|
||||
return QString("ignored");
|
||||
}
|
||||
|
||||
static ExplainingStep buildChildDiagnostic(const CXDiagnostic cxDiagnostic)
|
||||
{
|
||||
ExplainingStep diagnosticStep;
|
||||
QString type = cxDiagnosticType(cxDiagnostic);
|
||||
if (type == QStringLiteral("ignored"))
|
||||
return diagnosticStep;
|
||||
|
||||
const CXSourceLocation cxLocation = clang_getDiagnosticLocation(cxDiagnostic);
|
||||
diagnosticStep.location = diagLocationFromSourceLocation(cxLocation);
|
||||
diagnosticStep.message = type + ": " + fromCXString(clang_getDiagnosticSpelling(cxDiagnostic));
|
||||
return diagnosticStep;
|
||||
}
|
||||
|
||||
static bool isInvalidDiagnosticLocation(const Diagnostic &diagnostic, const ExplainingStep &child,
|
||||
const QString &nativeFilePath)
|
||||
{
|
||||
// When main file is considered included by itself - this diagnostic has invalid location.
|
||||
// This case usually happens when original diagnostic comes from system header but
|
||||
// has main file name set in the source location instead (which is incorrect).
|
||||
return child.message.indexOf(nativeFilePath) >= 0
|
||||
&& child.message.indexOf("in file included from") >= 0
|
||||
&& diagnostic.location.filePath == nativeFilePath;
|
||||
}
|
||||
|
||||
static ExplainingStep buildFixIt(const CXDiagnostic cxDiagnostic, unsigned index)
|
||||
{
|
||||
ExplainingStep fixItStep;
|
||||
CXSourceRange cxFixItRange;
|
||||
fixItStep.message = "fix-it: " + fromCXString(clang_getDiagnosticFixIt(cxDiagnostic, index,
|
||||
&cxFixItRange));
|
||||
fixItStep.location = diagLocationFromSourceLocation(clang_getRangeStart(cxFixItRange));
|
||||
fixItStep.ranges.push_back(fixItStep.location);
|
||||
fixItStep.ranges.push_back(diagLocationFromSourceLocation(clang_getRangeEnd(cxFixItRange)));
|
||||
return fixItStep;
|
||||
}
|
||||
|
||||
static Diagnostic buildDiagnostic(const CXDiagnostic cxDiagnostic, const QString &nativeFilePath)
|
||||
{
|
||||
Diagnostic diagnostic;
|
||||
diagnostic.type = cxDiagnosticType(cxDiagnostic);
|
||||
if (diagnostic.type == QStringLiteral("ignored"))
|
||||
return diagnostic;
|
||||
|
||||
const CXSourceLocation cxLocation = clang_getDiagnosticLocation(cxDiagnostic);
|
||||
if (clang_Location_isInSystemHeader(cxLocation))
|
||||
return diagnostic;
|
||||
|
||||
diagnostic.location = diagLocationFromSourceLocation(cxLocation);
|
||||
if (diagnostic.location.filePath != nativeFilePath)
|
||||
return diagnostic;
|
||||
|
||||
CXDiagnosticSet cxChildDiagnostics = clang_getChildDiagnostics(cxDiagnostic);
|
||||
Utils::ExecuteOnDestruction onBuildExit([&]() {
|
||||
clang_disposeDiagnosticSet(cxChildDiagnostics);
|
||||
});
|
||||
|
||||
for (unsigned i = 0; i < clang_getNumDiagnosticsInSet(cxChildDiagnostics); ++i) {
|
||||
CXDiagnostic cxDiagnostic = clang_getDiagnosticInSet(cxChildDiagnostics, i);
|
||||
Utils::ExecuteOnDestruction cleanUpDiagnostic([&]() {
|
||||
clang_disposeDiagnostic(cxDiagnostic);
|
||||
});
|
||||
const ExplainingStep diagnosticStep = buildChildDiagnostic(cxDiagnostic);
|
||||
if (diagnosticStep.isValid())
|
||||
continue;
|
||||
|
||||
if (isInvalidDiagnosticLocation(diagnostic, diagnosticStep, nativeFilePath))
|
||||
return diagnostic;
|
||||
|
||||
diagnostic.explainingSteps.push_back(diagnosticStep);
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < clang_getDiagnosticNumFixIts(cxDiagnostic); ++i)
|
||||
diagnostic.explainingSteps.push_back(buildFixIt(cxDiagnostic, i));
|
||||
|
||||
diagnostic.description = fromCXString(clang_getDiagnosticSpelling(cxDiagnostic));
|
||||
diagnostic.category = fromCXString(clang_getDiagnosticCategoryText(cxDiagnostic));
|
||||
|
||||
return diagnostic;
|
||||
}
|
||||
|
||||
QList<Diagnostic> ClangSerializedDiagnosticsReader::read(const QString &filePath,
|
||||
const QString &logFilePath)
|
||||
{
|
||||
QList<Diagnostic> list;
|
||||
CXLoadDiag_Error error;
|
||||
CXString errorString;
|
||||
|
||||
CXDiagnosticSet diagnostics = clang_loadDiagnostics(logFilePath.toStdString().c_str(),
|
||||
&error,
|
||||
&errorString);
|
||||
if (error != CXLoadDiag_None || !diagnostics)
|
||||
return list;
|
||||
|
||||
Utils::ExecuteOnDestruction onReadExit([&]() {
|
||||
clang_disposeDiagnosticSet(diagnostics);
|
||||
});
|
||||
|
||||
const QString nativeFilePath = QDir::toNativeSeparators(filePath);
|
||||
for (unsigned i = 0; i < clang_getNumDiagnosticsInSet(diagnostics); ++i) {
|
||||
CXDiagnostic cxDiagnostic = clang_getDiagnosticInSet(diagnostics, i);
|
||||
Utils::ExecuteOnDestruction cleanUpDiagnostic([&]() {
|
||||
clang_disposeDiagnostic(cxDiagnostic);
|
||||
});
|
||||
const Diagnostic diagnostic = buildDiagnostic(cxDiagnostic, nativeFilePath);
|
||||
if (!diagnostic.isValid())
|
||||
continue;
|
||||
|
||||
list.push_back(diagnostic);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace ClangTools
|
||||
|
@@ -30,6 +30,8 @@
|
||||
#include <QList>
|
||||
#include <QCoreApplication>
|
||||
|
||||
namespace Utils { class FileName; }
|
||||
|
||||
namespace ClangTools {
|
||||
namespace Internal {
|
||||
|
||||
@@ -38,6 +40,9 @@ class LogFileReader
|
||||
Q_DECLARE_TR_FUNCTIONS(ClangTools::Internal::LogFileReader)
|
||||
public:
|
||||
static QList<Diagnostic> readPlist(const QString &filePath, QString *errorMessage);
|
||||
static QList<Diagnostic> readSerialized(const QString &filePath,
|
||||
const QString &logFilePath,
|
||||
QString *errorMessage);
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
@@ -30,6 +30,7 @@
|
||||
#include "clangstaticanalyzerprojectsettingswidget.h"
|
||||
#include "clangstaticanalyzerruncontrol.h"
|
||||
#include "clangstaticanalyzertool.h"
|
||||
#include "clangtidyclazytool.h"
|
||||
|
||||
#ifdef WITH_TESTS
|
||||
#include "clangstaticanalyzerpreconfiguredsessiontests.h"
|
||||
@@ -104,6 +105,7 @@ class ClangToolsPluginPrivate
|
||||
{
|
||||
public:
|
||||
ClangStaticAnalyzerTool staticAnalyzerTool;
|
||||
ClangTidyClazyTool clangTidyClazyTool;
|
||||
ClangStaticAnalyzerOptionsPage optionsPage;
|
||||
};
|
||||
|
||||
|
@@ -94,6 +94,19 @@ void ClangDiagnosticConfig::setClangTidyMode(TidyMode mode)
|
||||
m_clangTidyMode = mode;
|
||||
}
|
||||
|
||||
QString ClangDiagnosticConfig::clangTidyChecks() const
|
||||
{
|
||||
QString checks;
|
||||
if (m_clangTidyMode == TidyMode::ChecksPrefixList) {
|
||||
checks = QStringLiteral("-*") + clangTidyChecksPrefixes();
|
||||
} else if (m_clangTidyMode == TidyMode::ChecksString) {
|
||||
checks = clangTidyChecksString();
|
||||
checks = checks.simplified();
|
||||
checks.replace(" ", "");
|
||||
}
|
||||
return checks;
|
||||
}
|
||||
|
||||
QString ClangDiagnosticConfig::clangTidyChecksPrefixes() const
|
||||
{
|
||||
return m_clangTidyChecksPrefixes;
|
||||
|
@@ -54,6 +54,8 @@ public:
|
||||
QStringList clangOptions() const;
|
||||
void setClangOptions(const QStringList &options);
|
||||
|
||||
QString clangTidyChecks() const;
|
||||
|
||||
QString clangTidyChecksPrefixes() const;
|
||||
void setClangTidyChecksPrefixes(const QString &checks);
|
||||
|
||||
|
@@ -4,6 +4,9 @@ TARGET = tst_clangtoolslogfilereader
|
||||
|
||||
DEFINES += SRCDIR=\\\"$$PWD/\\\"
|
||||
|
||||
LIBS += $$LIBCLANG_LIBS
|
||||
INCLUDEPATH += $$LLVM_INCLUDEPATH
|
||||
|
||||
SOURCES += \
|
||||
tst_clangtoolslogfilereader.cpp \
|
||||
$$PLUGINDIR/clangtoolsdiagnostic.cpp \
|
||||
|
@@ -3,8 +3,18 @@ import "../clangtoolsautotest.qbs" as ClangToolsAutotest
|
||||
|
||||
ClangToolsAutotest {
|
||||
name: "ClangToolsLogFileReader Autotest"
|
||||
|
||||
Depends { name: "libclang"; required: false }
|
||||
|
||||
cpp.defines: base.concat('SRCDIR="' + sourceDirectory + '"')
|
||||
|
||||
condition: libclang.present
|
||||
|
||||
cpp.includePaths: base.concat(libclang.llvmIncludeDir)
|
||||
cpp.libraryPaths: base.concat(libclang.llvmLibDir)
|
||||
cpp.dynamicLibraries: base.concat(libclang.llvmLibs)
|
||||
cpp.rpaths: base.concat(libclang.llvmLibDir)
|
||||
|
||||
Group {
|
||||
name: "sources from plugin"
|
||||
prefix: pluginDir + '/'
|
||||
|
Reference in New Issue
Block a user