diff options
24 files changed, 765 insertions, 17 deletions
diff --git a/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp b/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp index 05e5f37fa7..b9ac36fd76 100644 --- a/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp +++ b/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp @@ -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); } diff --git a/src/plugins/clangtools/clangstaticanalyzertool.cpp b/src/plugins/clangtools/clangstaticanalyzertool.cpp index a3873e16ca..651d794a27 100644 --- a/src/plugins/clangtools/clangstaticanalyzertool.cpp +++ b/src/plugins/clangtools/clangstaticanalyzertool.cpp @@ -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 diff --git a/src/plugins/clangtools/clangstaticanalyzertool.h b/src/plugins/clangtools/clangstaticanalyzertool.h index 8816e36a3c..e44330eddc 100644 --- a/src/plugins/clangtools/clangstaticanalyzertool.h +++ b/src/plugins/clangtools/clangstaticanalyzertool.h @@ -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: diff --git a/src/plugins/clangtools/clangtidyclazyruncontrol.cpp b/src/plugins/clangtools/clangtidyclazyruncontrol.cpp new file mode 100644 index 0000000000..a3ec062c22 --- /dev/null +++ b/src/plugins/clangtools/clangtidyclazyruncontrol.cpp @@ -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 + diff --git a/src/plugins/clangtools/clangtidyclazyruncontrol.h b/src/plugins/clangtools/clangtidyclazyruncontrol.h new file mode 100644 index 0000000000..59073608fe --- /dev/null +++ b/src/plugins/clangtools/clangtidyclazyruncontrol.h @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#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 diff --git a/src/plugins/clangtools/clangtidyclazyrunner.cpp b/src/plugins/clangtools/clangtidyclazyrunner.cpp new file mode 100644 index 0000000000..c3418525c4 --- /dev/null +++ b/src/plugins/clangtools/clangtidyclazyrunner.cpp @@ -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 diff --git a/src/plugins/clangtools/clangtidyclazyrunner.h b/src/plugins/clangtools/clangtidyclazyrunner.h new file mode 100644 index 0000000000..e723653192 --- /dev/null +++ b/src/plugins/clangtools/clangtidyclazyrunner.h @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#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 diff --git a/src/plugins/clangtools/clangtidyclazytool.cpp b/src/plugins/clangtools/clangtidyclazytool.cpp new file mode 100644 index 0000000000..ddd1cab9e2 --- /dev/null +++ b/src/plugins/clangtools/clangtidyclazytool.cpp @@ -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 + diff --git a/src/plugins/clangtools/clangtidyclazytool.h b/src/plugins/clangtools/clangtidyclazytool.h new file mode 100644 index 0000000000..e9e998b914 --- /dev/null +++ b/src/plugins/clangtools/clangtidyclazytool.h @@ -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 diff --git a/src/plugins/clangtools/clangtool.h b/src/plugins/clangtools/clangtool.h index b0fb84b68c..e7f11bb0cc 100644 --- a/src/plugins/clangtools/clangtool.h +++ b/src/plugins/clangtools/clangtool.h @@ -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. diff --git a/src/plugins/clangtools/clangtoolruncontrol.cpp b/src/plugins/clangtools/clangtoolruncontrol.cpp index 7c71c31da9..4e0b609dd1 100644 --- a/src/plugins/clangtools/clangtoolruncontrol.cpp +++ b/src/plugins/clangtools/clangtoolruncontrol.cpp @@ -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(); diff --git a/src/plugins/clangtools/clangtoolruncontrol.h b/src/plugins/clangtools/clangtoolruncontrol.h index 195a215090..c8339a40bc 100644 --- a/src/plugins/clangtools/clangtoolruncontrol.h +++ b/src/plugins/clangtools/clangtoolruncontrol.h @@ -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: diff --git a/src/plugins/clangtools/clangtoolrunner.cpp b/src/plugins/clangtools/clangtoolrunner.cpp index 6de55f85b5..79b027a930 100644 --- a/src/plugins/clangtools/clangtoolrunner.cpp +++ b/src/plugins/clangtools/clangtoolrunner.cpp @@ -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()); diff --git a/src/plugins/clangtools/clangtoolrunner.h b/src/plugins/clangtools/clangtoolrunner.h index 9cc3ee3185..5473890dd8 100644 --- a/src/plugins/clangtools/clangtoolrunner.h +++ b/src/plugins/clangtools/clangtoolrunner.h @@ -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: diff --git a/src/plugins/clangtools/clangtools.pro b/src/plugins/clangtools/clangtools.pro index 7b8a194a1b..021d31b5c6 100644 --- a/src/plugins/clangtools/clangtools.pro +++ b/src/plugins/clangtools/clangtools.pro @@ -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 \ diff --git a/src/plugins/clangtools/clangtools.qbs b/src/plugins/clangtools/clangtools.qbs index f744e4c604..8da24c41c4 100644 --- a/src/plugins/clangtools/clangtools.qbs +++ b/src/plugins/clangtools/clangtools.qbs @@ -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", diff --git a/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp b/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp index ac10caf268..2d6fb001b2 100644 --- a/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp +++ b/src/plugins/clangtools/clangtoolsdiagnosticmodel.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(); } diff --git a/src/plugins/clangtools/clangtoolslogfilereader.cpp b/src/plugins/clangtools/clangtoolslogfilereader.cpp index 22e4b6f161..23bda8a8d5 100644 --- a/src/plugins/clangtools/clangtoolslogfilereader.cpp +++ b/src/plugins/clangtools/clangtoolslogfilereader.cpp @@ -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 diff --git a/src/plugins/clangtools/clangtoolslogfilereader.h b/src/plugins/clangtools/clangtoolslogfilereader.h index 9b66dd854c..b93884e345 100644 --- a/src/plugins/clangtools/clangtoolslogfilereader.h +++ b/src/plugins/clangtools/clangtoolslogfilereader.h @@ -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 diff --git a/src/plugins/clangtools/clangtoolsplugin.cpp b/src/plugins/clangtools/clangtoolsplugin.cpp index 9ce1af6de6..0b08ab957b 100644 --- a/src/plugins/clangtools/clangtoolsplugin.cpp +++ b/src/plugins/clangtools/clangtoolsplugin.cpp @@ -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; }; diff --git a/src/plugins/cpptools/clangdiagnosticconfig.cpp b/src/plugins/cpptools/clangdiagnosticconfig.cpp index 724f4404e5..c760ed218a 100644 --- a/src/plugins/cpptools/clangdiagnosticconfig.cpp +++ b/src/plugins/cpptools/clangdiagnosticconfig.cpp @@ -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; diff --git a/src/plugins/cpptools/clangdiagnosticconfig.h b/src/plugins/cpptools/clangdiagnosticconfig.h index 36dd9f2dfd..6423aca181 100644 --- a/src/plugins/cpptools/clangdiagnosticconfig.h +++ b/src/plugins/cpptools/clangdiagnosticconfig.h @@ -54,6 +54,8 @@ public: QStringList clangOptions() const; void setClangOptions(const QStringList &options); + QString clangTidyChecks() const; + QString clangTidyChecksPrefixes() const; void setClangTidyChecksPrefixes(const QString &checks); diff --git a/tests/auto/clangtools/clangtoolslogfilereader/clangtoolslogfilereader.pro b/tests/auto/clangtools/clangtoolslogfilereader/clangtoolslogfilereader.pro index 5271b5f18e..2a72297f91 100644 --- a/tests/auto/clangtools/clangtoolslogfilereader/clangtoolslogfilereader.pro +++ b/tests/auto/clangtools/clangtoolslogfilereader/clangtoolslogfilereader.pro @@ -4,6 +4,9 @@ TARGET = tst_clangtoolslogfilereader DEFINES += SRCDIR=\\\"$$PWD/\\\" +LIBS += $$LIBCLANG_LIBS +INCLUDEPATH += $$LLVM_INCLUDEPATH + SOURCES += \ tst_clangtoolslogfilereader.cpp \ $$PLUGINDIR/clangtoolsdiagnostic.cpp \ diff --git a/tests/auto/clangtools/clangtoolslogfilereader/clangtoolslogfilereader.qbs b/tests/auto/clangtools/clangtoolslogfilereader/clangtoolslogfilereader.qbs index 993733954c..2a1a5b0a25 100644 --- a/tests/auto/clangtools/clangtoolslogfilereader/clangtoolslogfilereader.qbs +++ b/tests/auto/clangtools/clangtoolslogfilereader/clangtoolslogfilereader.qbs @@ -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 + '/' |