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:
Ivan Donchevskii
2018-01-17 15:08:30 +01:00
parent 4ec4f111cb
commit 219e23332e
24 changed files with 765 additions and 17 deletions

View File

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

View File

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

View File

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

View 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

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.
**
****************************************************************************/
#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

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.
**
****************************************************************************/
#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

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.
**
****************************************************************************/
#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

View 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

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.
**
****************************************************************************/
#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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -54,6 +54,8 @@ public:
QStringList clangOptions() const;
void setClangOptions(const QStringList &options);
QString clangTidyChecks() const;
QString clangTidyChecksPrefixes() const;
void setClangTidyChecksPrefixes(const QString &checks);

View File

@@ -4,6 +4,9 @@ TARGET = tst_clangtoolslogfilereader
DEFINES += SRCDIR=\\\"$$PWD/\\\"
LIBS += $$LIBCLANG_LIBS
INCLUDEPATH += $$LLVM_INCLUDEPATH
SOURCES += \
tst_clangtoolslogfilereader.cpp \
$$PLUGINDIR/clangtoolsdiagnostic.cpp \

View File

@@ -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 + '/'