forked from qt-creator/qt-creator
ClangTools: Add automatic clang tool runner for open documents
Fixes: QTCREATORBUG-23349 Change-Id: I81197180c9d69c7df6184f8fcbf05f2256eaf7f6 Reviewed-by: Christian Kandeler <christian.kandeler@qt.io> Reviewed-by: Christian Stenger <christian.stenger@qt.io> Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
This commit is contained in:
@@ -32,11 +32,13 @@ add_qtc_plugin(ClangTools
|
|||||||
clazychecks.ui
|
clazychecks.ui
|
||||||
diagnosticconfigswidget.cpp diagnosticconfigswidget.h
|
diagnosticconfigswidget.cpp diagnosticconfigswidget.h
|
||||||
diagnosticmark.cpp diagnosticmark.h
|
diagnosticmark.cpp diagnosticmark.h
|
||||||
|
documentclangtoolrunner.cpp documentclangtoolrunner.h
|
||||||
executableinfo.cpp executableinfo.h
|
executableinfo.cpp executableinfo.h
|
||||||
filterdialog.cpp filterdialog.h filterdialog.ui
|
filterdialog.cpp filterdialog.h filterdialog.ui
|
||||||
runsettingswidget.cpp runsettingswidget.h runsettingswidget.ui
|
runsettingswidget.cpp runsettingswidget.h runsettingswidget.ui
|
||||||
settingswidget.cpp settingswidget.h settingswidget.ui
|
settingswidget.cpp settingswidget.h settingswidget.ui
|
||||||
tidychecks.ui
|
tidychecks.ui
|
||||||
|
virtualfilesystemoverlay.cpp virtualfilesystemoverlay.h
|
||||||
)
|
)
|
||||||
|
|
||||||
extend_qtc_plugin(ClangTools
|
extend_qtc_plugin(ClangTools
|
||||||
|
@@ -80,6 +80,13 @@ static QStringList mainToolArguments(const QString &mainFilePath, const QString
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static QString virtualFileSystemOverlay(const QString &overlayFilePath)
|
||||||
|
{
|
||||||
|
if (overlayFilePath.isEmpty())
|
||||||
|
return {};
|
||||||
|
return "--vfsoverlay=" + overlayFilePath;
|
||||||
|
}
|
||||||
|
|
||||||
static QStringList clangArguments(const ClangDiagnosticConfig &diagnosticConfig,
|
static QStringList clangArguments(const ClangDiagnosticConfig &diagnosticConfig,
|
||||||
const QStringList &baseOptions)
|
const QStringList &baseOptions)
|
||||||
{
|
{
|
||||||
@@ -102,11 +109,11 @@ ClangTidyRunner::ClangTidyRunner(const ClangDiagnosticConfig &config, QObject *p
|
|||||||
setOutputFileFormat(OutputFileFormat::Yaml);
|
setOutputFileFormat(OutputFileFormat::Yaml);
|
||||||
setExecutable(clangTidyExecutable());
|
setExecutable(clangTidyExecutable());
|
||||||
setArgsCreator([this, config](const QStringList &baseOptions) {
|
setArgsCreator([this, config](const QStringList &baseOptions) {
|
||||||
return QStringList()
|
return QStringList() << tidyChecksArguments(config)
|
||||||
<< tidyChecksArguments(config)
|
<< mainToolArguments(fileToAnalyze(), outputFilePath())
|
||||||
<< mainToolArguments(fileToAnalyze(), outputFilePath())
|
<< virtualFileSystemOverlay(m_overlayFilePath)
|
||||||
<< "--"
|
<< "--"
|
||||||
<< clangArguments(config, baseOptions);
|
<< clangArguments(config, baseOptions);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -376,13 +376,6 @@ static RunSettings runSettings()
|
|||||||
return ClangToolsSettings::instance()->runSettings();
|
return ClangToolsSettings::instance()->runSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
static ClangDiagnosticConfig diagnosticConfig(const Utils::Id &diagConfigId)
|
|
||||||
{
|
|
||||||
const ClangDiagnosticConfigsModel configs = diagnosticConfigsModel();
|
|
||||||
QTC_ASSERT(configs.hasConfigWithId(diagConfigId), return ClangDiagnosticConfig());
|
|
||||||
return configs.configWithId(diagConfigId);
|
|
||||||
}
|
|
||||||
|
|
||||||
ClangTool *ClangTool::instance()
|
ClangTool *ClangTool::instance()
|
||||||
{
|
{
|
||||||
return s_instance;
|
return s_instance;
|
||||||
|
@@ -73,45 +73,6 @@ using namespace Utils;
|
|||||||
|
|
||||||
static Q_LOGGING_CATEGORY(LOG, "qtc.clangtools.runcontrol", QtWarningMsg)
|
static Q_LOGGING_CATEGORY(LOG, "qtc.clangtools.runcontrol", QtWarningMsg)
|
||||||
|
|
||||||
static QStringList splitArgs(QString &argsString)
|
|
||||||
{
|
|
||||||
QStringList result;
|
|
||||||
Utils::QtcProcess::ArgIterator it(&argsString);
|
|
||||||
while (it.next())
|
|
||||||
result.append(it.value());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static QStringList extraOptions(const char *environment)
|
|
||||||
{
|
|
||||||
if (!qEnvironmentVariableIsSet(environment))
|
|
||||||
return QStringList();
|
|
||||||
QString arguments = QString::fromLocal8Bit(qgetenv(environment));
|
|
||||||
return splitArgs(arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
static QStringList extraClangToolsPrependOptions()
|
|
||||||
{
|
|
||||||
constexpr char csaPrependOptions[] = "QTC_CLANG_CSA_CMD_PREPEND";
|
|
||||||
constexpr char toolsPrependOptions[] = "QTC_CLANG_TOOLS_CMD_PREPEND";
|
|
||||||
static const QStringList options = extraOptions(csaPrependOptions)
|
|
||||||
+ extraOptions(toolsPrependOptions);
|
|
||||||
if (!options.isEmpty())
|
|
||||||
qWarning() << "ClangTools options are prepended with " << options.toVector();
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
|
|
||||||
static QStringList extraClangToolsAppendOptions()
|
|
||||||
{
|
|
||||||
constexpr char csaAppendOptions[] = "QTC_CLANG_CSA_CMD_APPEND";
|
|
||||||
constexpr char toolsAppendOptions[] = "QTC_CLANG_TOOLS_CMD_APPEND";
|
|
||||||
static const QStringList options = extraOptions(csaAppendOptions)
|
|
||||||
+ extraOptions(toolsAppendOptions);
|
|
||||||
if (!options.isEmpty())
|
|
||||||
qWarning() << "ClangTools options are appended with " << options.toVector();
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace ClangTools {
|
namespace ClangTools {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
@@ -154,26 +115,21 @@ private:
|
|||||||
bool m_success = false;
|
bool m_success = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
static AnalyzeUnits toAnalyzeUnits(const FileInfos &fileInfos, const FilePath &clangIncludeDir,
|
AnalyzeUnit::AnalyzeUnit(const FileInfo &fileInfo,
|
||||||
const QString &clangVersion)
|
const FilePath &clangIncludeDir,
|
||||||
|
const QString &clangVersion)
|
||||||
{
|
{
|
||||||
AnalyzeUnits unitsToAnalyze;
|
CompilerOptionsBuilder optionsBuilder(*fileInfo.projectPart,
|
||||||
const UsePrecompiledHeaders usePrecompiledHeaders = CppTools::getPchUsage();
|
UseSystemHeader::No,
|
||||||
for (const FileInfo &fileInfo : fileInfos) {
|
UseTweakedHeaderPaths::Yes,
|
||||||
CompilerOptionsBuilder optionsBuilder(*fileInfo.projectPart,
|
UseLanguageDefines::No,
|
||||||
UseSystemHeader::No,
|
UseBuildSystemWarnings::No,
|
||||||
UseTweakedHeaderPaths::Yes,
|
clangVersion,
|
||||||
UseLanguageDefines::No,
|
clangIncludeDir.toString());
|
||||||
UseBuildSystemWarnings::No,
|
file = fileInfo.file.toString();
|
||||||
clangVersion,
|
arguments = extraClangToolsPrependOptions();
|
||||||
clangIncludeDir.toString());
|
arguments.append(optionsBuilder.build(fileInfo.kind, CppTools::getPchUsage()));
|
||||||
QStringList arguments = extraClangToolsPrependOptions();
|
arguments.append(extraClangToolsAppendOptions());
|
||||||
arguments.append(optionsBuilder.build(fileInfo.kind, usePrecompiledHeaders));
|
|
||||||
arguments.append(extraClangToolsAppendOptions());
|
|
||||||
unitsToAnalyze << AnalyzeUnit(fileInfo.file.toString(), arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
return unitsToAnalyze;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AnalyzeUnits ClangToolRunWorker::unitsToAnalyze(const FilePath &clangIncludeDir,
|
AnalyzeUnits ClangToolRunWorker::unitsToAnalyze(const FilePath &clangIncludeDir,
|
||||||
@@ -181,7 +137,10 @@ AnalyzeUnits ClangToolRunWorker::unitsToAnalyze(const FilePath &clangIncludeDir,
|
|||||||
{
|
{
|
||||||
QTC_ASSERT(m_projectInfo.isValid(), return AnalyzeUnits());
|
QTC_ASSERT(m_projectInfo.isValid(), return AnalyzeUnits());
|
||||||
|
|
||||||
return toAnalyzeUnits(m_fileInfos, clangIncludeDir, clangVersion);
|
AnalyzeUnits units;
|
||||||
|
for (const FileInfo &fileInfo : m_fileInfos)
|
||||||
|
units << AnalyzeUnit(fileInfo, clangIncludeDir, clangVersion);
|
||||||
|
return units;
|
||||||
}
|
}
|
||||||
|
|
||||||
static QDebug operator<<(QDebug debug, const Utils::Environment &environment)
|
static QDebug operator<<(QDebug debug, const Utils::Environment &environment)
|
||||||
@@ -292,6 +251,8 @@ void ClangToolRunWorker::start()
|
|||||||
getClangIncludeDirAndVersion(runControl()->runnable().executable);
|
getClangIncludeDirAndVersion(runControl()->runnable().executable);
|
||||||
const AnalyzeUnits unitsToProcess = unitsToAnalyze(clangIncludeDirAndVersion.first,
|
const AnalyzeUnits unitsToProcess = unitsToAnalyze(clangIncludeDirAndVersion.first,
|
||||||
clangIncludeDirAndVersion.second);
|
clangIncludeDirAndVersion.second);
|
||||||
|
qCDebug(LOG) << Q_FUNC_INFO << runControl()->runnable().executable
|
||||||
|
<< clangIncludeDirAndVersion.first << clangIncludeDirAndVersion.second;
|
||||||
qCDebug(LOG) << "Files to process:" << unitsToProcess;
|
qCDebug(LOG) << "Files to process:" << unitsToProcess;
|
||||||
|
|
||||||
m_queue.clear();
|
m_queue.clear();
|
||||||
|
@@ -46,8 +46,9 @@ class ClangToolRunner;
|
|||||||
class ProjectBuilder;
|
class ProjectBuilder;
|
||||||
|
|
||||||
struct AnalyzeUnit {
|
struct AnalyzeUnit {
|
||||||
AnalyzeUnit(const QString &file, const QStringList &options)
|
AnalyzeUnit(const FileInfo &fileInfo,
|
||||||
: file(file), arguments(options) {}
|
const Utils::FilePath &clangResourceDir,
|
||||||
|
const QString &clangVersion);
|
||||||
|
|
||||||
QString file;
|
QString file;
|
||||||
QStringList arguments; // without file itself and "-o somePath"
|
QStringList arguments; // without file itself and "-o somePath"
|
||||||
|
@@ -75,7 +75,15 @@ void ClangToolRunner::init(const QString &outputDirPath,
|
|||||||
|
|
||||||
ClangToolRunner::~ClangToolRunner()
|
ClangToolRunner::~ClangToolRunner()
|
||||||
{
|
{
|
||||||
Utils::SynchronousProcess::stopProcess(m_process);
|
if (m_process.state() != QProcess::NotRunning) {
|
||||||
|
// asking politly to terminate costs ~300 ms on windows so skip the courtasy and direct kill the process
|
||||||
|
if (Utils::HostOsInfo::isWindowsHost()) {
|
||||||
|
m_process.kill();
|
||||||
|
m_process.waitForFinished(100);
|
||||||
|
} else {
|
||||||
|
Utils::SynchronousProcess::stopProcess(m_process);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static QString createOutputFilePath(const QString &dirPath, const QString &fileToAnalyze)
|
static QString createOutputFilePath(const QString &dirPath, const QString &fileToAnalyze)
|
||||||
|
@@ -52,6 +52,7 @@ public:
|
|||||||
void setExecutable(const QString &executable) { m_executable = executable; }
|
void setExecutable(const QString &executable) { m_executable = executable; }
|
||||||
void setArgsCreator(const ArgsCreator &argsCreator) { m_argsCreator = argsCreator; }
|
void setArgsCreator(const ArgsCreator &argsCreator) { m_argsCreator = argsCreator; }
|
||||||
void setOutputFileFormat(const OutputFileFormat &format) { m_outputFileFormat = format; }
|
void setOutputFileFormat(const OutputFileFormat &format) { m_outputFileFormat = format; }
|
||||||
|
void setVFSOverlay(const QString overlayFilePath) { m_overlayFilePath = overlayFilePath; }
|
||||||
|
|
||||||
QString name() const { return m_name; }
|
QString name() const { return m_name; }
|
||||||
QString executable() const { return m_executable; }
|
QString executable() const { return m_executable; }
|
||||||
@@ -68,6 +69,9 @@ signals:
|
|||||||
void finishedWithSuccess(const QString &fileToAnalyze);
|
void finishedWithSuccess(const QString &fileToAnalyze);
|
||||||
void finishedWithFailure(const QString &errorMessage, const QString &errorDetails);
|
void finishedWithFailure(const QString &errorMessage, const QString &errorDetails);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QString m_overlayFilePath;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onProcessOutput();
|
void onProcessOutput();
|
||||||
void onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
void onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
||||||
|
@@ -30,10 +30,12 @@ SOURCES += \
|
|||||||
clangtoolsutils.cpp \
|
clangtoolsutils.cpp \
|
||||||
diagnosticconfigswidget.cpp \
|
diagnosticconfigswidget.cpp \
|
||||||
diagnosticmark.cpp \
|
diagnosticmark.cpp \
|
||||||
|
documentclangtoolrunner.cpp \
|
||||||
executableinfo.cpp \
|
executableinfo.cpp \
|
||||||
filterdialog.cpp \
|
filterdialog.cpp \
|
||||||
runsettingswidget.cpp \
|
runsettingswidget.cpp \
|
||||||
settingswidget.cpp \
|
settingswidget.cpp \
|
||||||
|
virtualfilesystemoverlay.cpp \
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
clangfileinfo.h \
|
clangfileinfo.h \
|
||||||
@@ -56,10 +58,12 @@ HEADERS += \
|
|||||||
clangtoolsutils.h \
|
clangtoolsutils.h \
|
||||||
diagnosticconfigswidget.h \
|
diagnosticconfigswidget.h \
|
||||||
diagnosticmark.h \
|
diagnosticmark.h \
|
||||||
|
documentclangtoolrunner.h \
|
||||||
executableinfo.h \
|
executableinfo.h \
|
||||||
filterdialog.h \
|
filterdialog.h \
|
||||||
runsettingswidget.h \
|
runsettingswidget.h \
|
||||||
settingswidget.h \
|
settingswidget.h \
|
||||||
|
virtualfilesystemoverlay.h \
|
||||||
|
|
||||||
FORMS += \
|
FORMS += \
|
||||||
clangselectablefilesdialog.ui \
|
clangselectablefilesdialog.ui \
|
||||||
|
@@ -68,6 +68,8 @@ QtcPlugin {
|
|||||||
"diagnosticconfigswidget.h",
|
"diagnosticconfigswidget.h",
|
||||||
"diagnosticmark.cpp",
|
"diagnosticmark.cpp",
|
||||||
"diagnosticmark.h",
|
"diagnosticmark.h",
|
||||||
|
"documentclangtoolrunner.cpp",
|
||||||
|
"documentclangtoolrunner.h",
|
||||||
"executableinfo.cpp",
|
"executableinfo.cpp",
|
||||||
"executableinfo.h",
|
"executableinfo.h",
|
||||||
"filterdialog.cpp",
|
"filterdialog.cpp",
|
||||||
@@ -80,6 +82,8 @@ QtcPlugin {
|
|||||||
"settingswidget.h",
|
"settingswidget.h",
|
||||||
"settingswidget.ui",
|
"settingswidget.ui",
|
||||||
"tidychecks.ui",
|
"tidychecks.ui",
|
||||||
|
"virtualfilesystemoverlay.cpp",
|
||||||
|
"virtualfilesystemoverlay.h",
|
||||||
]
|
]
|
||||||
|
|
||||||
Group {
|
Group {
|
||||||
|
@@ -29,6 +29,7 @@
|
|||||||
#include "clangtoolsconstants.h"
|
#include "clangtoolsconstants.h"
|
||||||
#include "clangtoolsprojectsettings.h"
|
#include "clangtoolsprojectsettings.h"
|
||||||
#include "clangtoolsprojectsettingswidget.h"
|
#include "clangtoolsprojectsettingswidget.h"
|
||||||
|
#include "documentclangtoolrunner.h"
|
||||||
#include "settingswidget.h"
|
#include "settingswidget.h"
|
||||||
|
|
||||||
#ifdef WITH_TESTS
|
#ifdef WITH_TESTS
|
||||||
@@ -84,6 +85,7 @@ class ClangToolsPluginPrivate
|
|||||||
public:
|
public:
|
||||||
ClangTool clangTool;
|
ClangTool clangTool;
|
||||||
ClangToolsOptionsPage optionsPage;
|
ClangToolsOptionsPage optionsPage;
|
||||||
|
QMap<Core::IDocument *, DocumentClangToolRunner *> documentRunners;
|
||||||
};
|
};
|
||||||
|
|
||||||
ClangToolsPlugin::~ClangToolsPlugin()
|
ClangToolsPlugin::~ClangToolsPlugin()
|
||||||
@@ -111,9 +113,28 @@ bool ClangToolsPlugin::initialize(const QStringList &arguments, QString *errorSt
|
|||||||
panelFactory->setCreateWidgetFunction([](Project *project) { return new ProjectSettingsWidget(project); });
|
panelFactory->setCreateWidgetFunction([](Project *project) { return new ProjectSettingsWidget(project); });
|
||||||
ProjectPanelFactory::registerFactory(panelFactory);
|
ProjectPanelFactory::registerFactory(panelFactory);
|
||||||
|
|
||||||
|
connect(Core::EditorManager::instance(),
|
||||||
|
&Core::EditorManager::currentEditorChanged,
|
||||||
|
this,
|
||||||
|
&ClangToolsPlugin::onCurrentEditorChanged);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClangToolsPlugin::onCurrentEditorChanged()
|
||||||
|
{
|
||||||
|
for (Core::IEditor *editor : Core::EditorManager::visibleEditors()) {
|
||||||
|
IDocument *document = editor->document();
|
||||||
|
if (d->documentRunners.contains(document))
|
||||||
|
continue;
|
||||||
|
auto runner = new DocumentClangToolRunner(document);
|
||||||
|
connect(runner, &DocumentClangToolRunner::destroyed, this, [this, document]() {
|
||||||
|
d->documentRunners.remove(document);
|
||||||
|
});
|
||||||
|
d->documentRunners[document] = runner;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ClangToolsPlugin::registerAnalyzeActions()
|
void ClangToolsPlugin::registerAnalyzeActions()
|
||||||
{
|
{
|
||||||
ActionManager::registerAction(d->clangTool.startAction(), Constants::RUN_ON_PROJECT);
|
ActionManager::registerAction(d->clangTool.startAction(), Constants::RUN_ON_PROJECT);
|
||||||
|
@@ -27,6 +27,7 @@
|
|||||||
|
|
||||||
#include <extensionsystem/iplugin.h>
|
#include <extensionsystem/iplugin.h>
|
||||||
|
|
||||||
|
namespace Core { class IDocument; }
|
||||||
namespace ProjectExplorer { class ProjectPanelFactory; }
|
namespace ProjectExplorer { class ProjectPanelFactory; }
|
||||||
|
|
||||||
namespace ClangTools {
|
namespace ClangTools {
|
||||||
@@ -46,6 +47,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
bool initialize(const QStringList &arguments, QString *errorString) final;
|
bool initialize(const QStringList &arguments, QString *errorString) final;
|
||||||
void registerAnalyzeActions();
|
void registerAnalyzeActions();
|
||||||
|
void onCurrentEditorChanged();
|
||||||
|
|
||||||
QVector<QObject *> createTestObjects() const final;
|
QVector<QObject *> createTestObjects() const final;
|
||||||
|
|
||||||
|
@@ -48,6 +48,8 @@ ClangToolsProjectSettings::ClangToolsProjectSettings(ProjectExplorer::Project *p
|
|||||||
: m_project(project)
|
: m_project(project)
|
||||||
{
|
{
|
||||||
load();
|
load();
|
||||||
|
connect(this, &ClangToolsProjectSettings::suppressedDiagnosticsChanged,
|
||||||
|
this, &ClangToolsProjectSettings::changed);
|
||||||
connect(project, &ProjectExplorer::Project::settingsLoaded,
|
connect(project, &ProjectExplorer::Project::settingsLoaded,
|
||||||
this, &ClangToolsProjectSettings::load);
|
this, &ClangToolsProjectSettings::load);
|
||||||
connect(project, &ProjectExplorer::Project::aboutToSaveSettings, this,
|
connect(project, &ProjectExplorer::Project::aboutToSaveSettings, this,
|
||||||
@@ -59,6 +61,38 @@ ClangToolsProjectSettings::~ClangToolsProjectSettings()
|
|||||||
store();
|
store();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClangToolsProjectSettings::setUseGlobalSettings(bool useGlobalSettings)
|
||||||
|
{
|
||||||
|
if (m_useGlobalSettings == useGlobalSettings)
|
||||||
|
return;
|
||||||
|
m_useGlobalSettings = useGlobalSettings;
|
||||||
|
emit changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClangToolsProjectSettings::setRunSettings(const RunSettings &settings)
|
||||||
|
{
|
||||||
|
if (m_runSettings == settings)
|
||||||
|
return;
|
||||||
|
m_runSettings = settings;
|
||||||
|
emit changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClangToolsProjectSettings::setSelectedDirs(const QSet<Utils::FilePath> &value)
|
||||||
|
{
|
||||||
|
if (m_selectedDirs == value)
|
||||||
|
return;
|
||||||
|
m_selectedDirs = value;
|
||||||
|
emit changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClangToolsProjectSettings::setSelectedFiles(const QSet<Utils::FilePath> &value)
|
||||||
|
{
|
||||||
|
if (m_selectedFiles == value)
|
||||||
|
return;
|
||||||
|
m_selectedFiles = value;
|
||||||
|
emit changed();
|
||||||
|
}
|
||||||
|
|
||||||
void ClangToolsProjectSettings::addSuppressedDiagnostic(const SuppressedDiagnostic &diag)
|
void ClangToolsProjectSettings::addSuppressedDiagnostic(const SuppressedDiagnostic &diag)
|
||||||
{
|
{
|
||||||
QTC_ASSERT(!m_suppressedDiagnostics.contains(diag), return);
|
QTC_ASSERT(!m_suppressedDiagnostics.contains(diag), return);
|
||||||
|
@@ -70,16 +70,16 @@ public:
|
|||||||
~ClangToolsProjectSettings() override;
|
~ClangToolsProjectSettings() override;
|
||||||
|
|
||||||
bool useGlobalSettings() const { return m_useGlobalSettings; }
|
bool useGlobalSettings() const { return m_useGlobalSettings; }
|
||||||
void setUseGlobalSettings(bool useGlobalSettings) { m_useGlobalSettings = useGlobalSettings; }
|
void setUseGlobalSettings(bool useGlobalSettings);
|
||||||
|
|
||||||
RunSettings runSettings() const { return m_runSettings; }
|
RunSettings runSettings() const { return m_runSettings; }
|
||||||
void setRunSettings(const RunSettings &settings) { m_runSettings = settings; }
|
void setRunSettings(const RunSettings &settings);
|
||||||
|
|
||||||
QSet<Utils::FilePath> selectedDirs() const { return m_selectedDirs; }
|
QSet<Utils::FilePath> selectedDirs() const { return m_selectedDirs; }
|
||||||
void setSelectedDirs(const QSet<Utils::FilePath> &value) { m_selectedDirs = value; }
|
void setSelectedDirs(const QSet<Utils::FilePath> &value);
|
||||||
|
|
||||||
QSet<Utils::FilePath> selectedFiles() const { return m_selectedFiles; }
|
QSet<Utils::FilePath> selectedFiles() const { return m_selectedFiles; }
|
||||||
void setSelectedFiles(const QSet<Utils::FilePath> &value) { m_selectedFiles = value; }
|
void setSelectedFiles(const QSet<Utils::FilePath> &value);
|
||||||
|
|
||||||
SuppressedDiagnosticsList suppressedDiagnostics() const { return m_suppressedDiagnostics; }
|
SuppressedDiagnosticsList suppressedDiagnostics() const { return m_suppressedDiagnostics; }
|
||||||
void addSuppressedDiagnostic(const SuppressedDiagnostic &diag);
|
void addSuppressedDiagnostic(const SuppressedDiagnostic &diag);
|
||||||
@@ -91,6 +91,7 @@ public:
|
|||||||
|
|
||||||
signals:
|
signals:
|
||||||
void suppressedDiagnosticsChanged();
|
void suppressedDiagnosticsChanged();
|
||||||
|
void changed();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void load();
|
void load();
|
||||||
|
@@ -42,7 +42,7 @@ static const char clazyStandaloneExecutableKey[] = "ClazyStandaloneExecutable";
|
|||||||
|
|
||||||
static const char parallelJobsKey[] = "ParallelJobs";
|
static const char parallelJobsKey[] = "ParallelJobs";
|
||||||
static const char buildBeforeAnalysisKey[] = "BuildBeforeAnalysis";
|
static const char buildBeforeAnalysisKey[] = "BuildBeforeAnalysis";
|
||||||
|
static const char analyzeOpenFilesKey[] = "AnalyzeOpenFiles";
|
||||||
static const char oldDiagnosticConfigIdKey[] = "diagnosticConfigId";
|
static const char oldDiagnosticConfigIdKey[] = "diagnosticConfigId";
|
||||||
|
|
||||||
using namespace CppTools;
|
using namespace CppTools;
|
||||||
@@ -66,6 +66,7 @@ void RunSettings::fromMap(const QVariantMap &map, const QString &prefix)
|
|||||||
m_diagnosticConfigId = Utils::Id::fromSetting(map.value(prefix + diagnosticConfigIdKey));
|
m_diagnosticConfigId = Utils::Id::fromSetting(map.value(prefix + diagnosticConfigIdKey));
|
||||||
m_parallelJobs = map.value(prefix + parallelJobsKey).toInt();
|
m_parallelJobs = map.value(prefix + parallelJobsKey).toInt();
|
||||||
m_buildBeforeAnalysis = map.value(prefix + buildBeforeAnalysisKey).toBool();
|
m_buildBeforeAnalysis = map.value(prefix + buildBeforeAnalysisKey).toBool();
|
||||||
|
m_analyzeOpenFiles = map.value(prefix + analyzeOpenFilesKey).toBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RunSettings::toMap(QVariantMap &map, const QString &prefix) const
|
void RunSettings::toMap(QVariantMap &map, const QString &prefix) const
|
||||||
@@ -73,6 +74,7 @@ void RunSettings::toMap(QVariantMap &map, const QString &prefix) const
|
|||||||
map.insert(prefix + diagnosticConfigIdKey, m_diagnosticConfigId.toSetting());
|
map.insert(prefix + diagnosticConfigIdKey, m_diagnosticConfigId.toSetting());
|
||||||
map.insert(prefix + parallelJobsKey, m_parallelJobs);
|
map.insert(prefix + parallelJobsKey, m_parallelJobs);
|
||||||
map.insert(prefix + buildBeforeAnalysisKey, m_buildBeforeAnalysis);
|
map.insert(prefix + buildBeforeAnalysisKey, m_buildBeforeAnalysis);
|
||||||
|
map.insert(prefix + analyzeOpenFilesKey, m_analyzeOpenFiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::Id RunSettings::diagnosticConfigId() const
|
Utils::Id RunSettings::diagnosticConfigId() const
|
||||||
@@ -82,6 +84,14 @@ Utils::Id RunSettings::diagnosticConfigId() const
|
|||||||
return m_diagnosticConfigId;
|
return m_diagnosticConfigId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RunSettings::operator==(const RunSettings &other) const
|
||||||
|
{
|
||||||
|
return m_diagnosticConfigId == other.m_diagnosticConfigId
|
||||||
|
&& m_parallelJobs == other.m_parallelJobs
|
||||||
|
&& m_buildBeforeAnalysis == other.m_buildBeforeAnalysis
|
||||||
|
&& m_analyzeOpenFiles == other.m_analyzeOpenFiles;
|
||||||
|
}
|
||||||
|
|
||||||
ClangToolsSettings::ClangToolsSettings()
|
ClangToolsSettings::ClangToolsSettings()
|
||||||
{
|
{
|
||||||
readSettings();
|
readSettings();
|
||||||
@@ -154,6 +164,7 @@ void ClangToolsSettings::readSettings()
|
|||||||
defaults.insert(diagnosticConfigIdKey, defaultDiagnosticId().toSetting());
|
defaults.insert(diagnosticConfigIdKey, defaultDiagnosticId().toSetting());
|
||||||
defaults.insert(parallelJobsKey, m_runSettings.parallelJobs());
|
defaults.insert(parallelJobsKey, m_runSettings.parallelJobs());
|
||||||
defaults.insert(buildBeforeAnalysisKey, m_runSettings.buildBeforeAnalysis());
|
defaults.insert(buildBeforeAnalysisKey, m_runSettings.buildBeforeAnalysis());
|
||||||
|
defaults.insert(analyzeOpenFilesKey, m_runSettings.analyzeOpenFiles());
|
||||||
map = defaults;
|
map = defaults;
|
||||||
for (QVariantMap::ConstIterator it = defaults.constBegin(); it != defaults.constEnd(); ++it)
|
for (QVariantMap::ConstIterator it = defaults.constBegin(); it != defaults.constEnd(); ++it)
|
||||||
map.insert(it.key(), s->value(it.key(), it.value()));
|
map.insert(it.key(), s->value(it.key(), it.value()));
|
||||||
|
@@ -54,10 +54,16 @@ public:
|
|||||||
int parallelJobs() const { return m_parallelJobs; }
|
int parallelJobs() const { return m_parallelJobs; }
|
||||||
void setParallelJobs(int jobs) { m_parallelJobs = jobs; }
|
void setParallelJobs(int jobs) { m_parallelJobs = jobs; }
|
||||||
|
|
||||||
|
bool analyzeOpenFiles() const { return m_analyzeOpenFiles; }
|
||||||
|
void setAnalyzeOpenFiles(bool analyzeOpenFiles) { m_analyzeOpenFiles = analyzeOpenFiles; }
|
||||||
|
|
||||||
|
bool operator==(const RunSettings &other) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Utils::Id m_diagnosticConfigId;
|
Utils::Id m_diagnosticConfigId;
|
||||||
int m_parallelJobs = -1;
|
int m_parallelJobs = -1;
|
||||||
bool m_buildBeforeAnalysis = true;
|
bool m_buildBeforeAnalysis = true;
|
||||||
|
bool m_analyzeOpenFiles = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ClangToolsSettings : public QObject
|
class ClangToolsSettings : public QObject
|
||||||
|
@@ -35,6 +35,7 @@
|
|||||||
#include <cpptools/cpptoolsreuse.h>
|
#include <cpptools/cpptoolsreuse.h>
|
||||||
#include <projectexplorer/projectexplorerconstants.h>
|
#include <projectexplorer/projectexplorerconstants.h>
|
||||||
|
|
||||||
|
#include <utils/qtcprocess.h>
|
||||||
#include <utils/checkablemessagebox.h>
|
#include <utils/checkablemessagebox.h>
|
||||||
#include <utils/environment.h>
|
#include <utils/environment.h>
|
||||||
#include <utils/hostosinfo.h>
|
#include <utils/hostosinfo.h>
|
||||||
@@ -304,5 +305,51 @@ QString documentationUrl(const QString &checkName)
|
|||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ClangDiagnosticConfig diagnosticConfig(const Utils::Id &diagConfigId)
|
||||||
|
{
|
||||||
|
const ClangDiagnosticConfigsModel configs = diagnosticConfigsModel();
|
||||||
|
QTC_ASSERT(configs.hasConfigWithId(diagConfigId), return ClangDiagnosticConfig());
|
||||||
|
return configs.configWithId(diagConfigId);
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList splitArgs(QString &argsString)
|
||||||
|
{
|
||||||
|
QStringList result;
|
||||||
|
Utils::QtcProcess::ArgIterator it(&argsString);
|
||||||
|
while (it.next())
|
||||||
|
result.append(it.value());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList extraOptions(const char *envVar)
|
||||||
|
{
|
||||||
|
if (!qEnvironmentVariableIsSet(envVar))
|
||||||
|
return QStringList();
|
||||||
|
QString arguments = QString::fromLocal8Bit(qgetenv(envVar));
|
||||||
|
return splitArgs(arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList extraClangToolsPrependOptions()
|
||||||
|
{
|
||||||
|
constexpr char csaPrependOptions[] = "QTC_CLANG_CSA_CMD_PREPEND";
|
||||||
|
constexpr char toolsPrependOptions[] = "QTC_CLANG_TOOLS_CMD_PREPEND";
|
||||||
|
static const QStringList options = extraOptions(csaPrependOptions)
|
||||||
|
+ extraOptions(toolsPrependOptions);
|
||||||
|
if (!options.isEmpty())
|
||||||
|
qWarning() << "ClangTools options are prepended with " << options.toVector();
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList extraClangToolsAppendOptions()
|
||||||
|
{
|
||||||
|
constexpr char csaAppendOptions[] = "QTC_CLANG_CSA_CMD_APPEND";
|
||||||
|
constexpr char toolsAppendOptions[] = "QTC_CLANG_TOOLS_CMD_APPEND";
|
||||||
|
static const QStringList options = extraOptions(csaAppendOptions)
|
||||||
|
+ extraOptions(toolsAppendOptions);
|
||||||
|
if (!options.isEmpty())
|
||||||
|
qWarning() << "ClangTools options are appended with " << options.toVector();
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
} // namespace ClangTools
|
} // namespace ClangTools
|
||||||
|
@@ -80,5 +80,10 @@ CppTools::ClangDiagnosticConfigsModel diagnosticConfigsModel();
|
|||||||
CppTools::ClangDiagnosticConfigsModel diagnosticConfigsModel(
|
CppTools::ClangDiagnosticConfigsModel diagnosticConfigsModel(
|
||||||
const CppTools::ClangDiagnosticConfigs &customConfigs);
|
const CppTools::ClangDiagnosticConfigs &customConfigs);
|
||||||
|
|
||||||
|
CppTools::ClangDiagnosticConfig diagnosticConfig(const Utils::Id &diagConfigId);
|
||||||
|
|
||||||
|
QStringList extraClangToolsPrependOptions();
|
||||||
|
QStringList extraClangToolsAppendOptions();
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
} // namespace ClangTools
|
} // namespace ClangTools
|
||||||
|
@@ -41,6 +41,8 @@ public:
|
|||||||
void disable();
|
void disable();
|
||||||
bool enabled() const;
|
bool enabled() const;
|
||||||
|
|
||||||
|
QString source;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const Diagnostic m_diagnostic;
|
const Diagnostic m_diagnostic;
|
||||||
bool m_enabled = true;
|
bool m_enabled = true;
|
||||||
|
314
src/plugins/clangtools/documentclangtoolrunner.cpp
Normal file
314
src/plugins/clangtools/documentclangtoolrunner.cpp
Normal file
@@ -0,0 +1,314 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 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 "documentclangtoolrunner.h"
|
||||||
|
|
||||||
|
#include "clangfileinfo.h"
|
||||||
|
#include "clangtidyclazyrunner.h"
|
||||||
|
#include "clangtoolruncontrol.h"
|
||||||
|
#include "clangtoolsprojectsettings.h"
|
||||||
|
#include "clangtoolsutils.h"
|
||||||
|
#include "diagnosticmark.h"
|
||||||
|
#include "executableinfo.h"
|
||||||
|
#include "virtualfilesystemoverlay.h"
|
||||||
|
|
||||||
|
#include <coreplugin/documentmanager.h>
|
||||||
|
#include <coreplugin/editormanager/editormanager.h>
|
||||||
|
#include <coreplugin/editormanager/ieditor.h>
|
||||||
|
#include <cpptools/cppmodelmanager.h>
|
||||||
|
#include <projectexplorer/buildtargettype.h>
|
||||||
|
#include <projectexplorer/session.h>
|
||||||
|
#include <projectexplorer/target.h>
|
||||||
|
#include <texteditor/textdocument.h>
|
||||||
|
#include <texteditor/textmark.h>
|
||||||
|
#include <utils/qtcassert.h>
|
||||||
|
#include <utils/utilsicons.h>
|
||||||
|
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
|
||||||
|
static Q_LOGGING_CATEGORY(LOG, "qtc.clangtools.cftr", QtWarningMsg)
|
||||||
|
|
||||||
|
namespace ClangTools {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
DocumentClangToolRunner::DocumentClangToolRunner(Core::IDocument *document)
|
||||||
|
: QObject(document)
|
||||||
|
, m_document(document)
|
||||||
|
, m_temporaryDir("clangtools-single-XXXXXX")
|
||||||
|
{
|
||||||
|
using namespace CppTools;
|
||||||
|
|
||||||
|
m_runTimer.setInterval(500);
|
||||||
|
m_runTimer.setSingleShot(true);
|
||||||
|
|
||||||
|
connect(m_document,
|
||||||
|
&Core::IDocument::contentsChanged,
|
||||||
|
this,
|
||||||
|
&DocumentClangToolRunner::scheduleRun);
|
||||||
|
connect(CppModelManager::instance(),
|
||||||
|
&CppModelManager::projectPartsUpdated,
|
||||||
|
this,
|
||||||
|
&DocumentClangToolRunner::scheduleRun);
|
||||||
|
connect(ClangToolsSettings::instance(),
|
||||||
|
&ClangToolsSettings::changed,
|
||||||
|
this,
|
||||||
|
&DocumentClangToolRunner::scheduleRun);
|
||||||
|
connect(&m_runTimer, &QTimer::timeout, this, &DocumentClangToolRunner::run);
|
||||||
|
run();
|
||||||
|
}
|
||||||
|
|
||||||
|
DocumentClangToolRunner::~DocumentClangToolRunner()
|
||||||
|
{
|
||||||
|
cancel();
|
||||||
|
qDeleteAll(m_marks);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DocumentClangToolRunner::scheduleRun()
|
||||||
|
{
|
||||||
|
for (DiagnosticMark *mark : m_marks)
|
||||||
|
mark->disable();
|
||||||
|
m_runTimer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
static ProjectExplorer::Project *findProject(const Utils::FilePath &file)
|
||||||
|
{
|
||||||
|
ProjectExplorer::Project *project = ProjectExplorer::SessionManager::projectForFile(file);
|
||||||
|
return project ? project : ProjectExplorer::SessionManager::startupProject();
|
||||||
|
}
|
||||||
|
|
||||||
|
static VirtualFileSystemOverlay &vfso()
|
||||||
|
{
|
||||||
|
static VirtualFileSystemOverlay overlay("clangtools-vfso-XXXXXX");
|
||||||
|
return overlay;
|
||||||
|
}
|
||||||
|
|
||||||
|
static FileInfo getFileInfo(const Utils::FilePath &file, ProjectExplorer::Project *project)
|
||||||
|
{
|
||||||
|
CppTools::ProjectInfo projectInfo = CppTools::CppModelManager::instance()->projectInfo(project);
|
||||||
|
if (!projectInfo.isValid())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
FileInfo candidate;
|
||||||
|
for (const CppTools::ProjectPart::Ptr &projectPart : projectInfo.projectParts()) {
|
||||||
|
QTC_ASSERT(projectPart, continue);
|
||||||
|
|
||||||
|
for (const CppTools::ProjectFile &projectFile : qAsConst(projectPart->files)) {
|
||||||
|
QTC_ASSERT(projectFile.kind != CppTools::ProjectFile::Unclassified, continue);
|
||||||
|
QTC_ASSERT(projectFile.kind != CppTools::ProjectFile::Unsupported, continue);
|
||||||
|
if (projectFile.path == CppTools::CppModelManager::configurationFileName())
|
||||||
|
continue;
|
||||||
|
if (file.toString() != projectFile.path)
|
||||||
|
continue;
|
||||||
|
if (!projectFile.active)
|
||||||
|
continue;
|
||||||
|
if (projectPart->buildTargetType != ProjectExplorer::BuildTargetType::Unknown) {
|
||||||
|
// found the best candidate, early return
|
||||||
|
return FileInfo(Utils::FilePath::fromString(projectFile.path),
|
||||||
|
projectFile.kind,
|
||||||
|
projectPart);
|
||||||
|
}
|
||||||
|
if (candidate.projectPart.isNull()) {
|
||||||
|
// found at least something but keep looking for better candidates
|
||||||
|
candidate = FileInfo(Utils::FilePath::fromString(projectFile.path),
|
||||||
|
projectFile.kind,
|
||||||
|
projectPart);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Utils::Environment projectBuildEnvironment(ProjectExplorer::Project *project)
|
||||||
|
{
|
||||||
|
Utils::Environment env;
|
||||||
|
if (ProjectExplorer::Target *target = project->activeTarget()) {
|
||||||
|
if (ProjectExplorer::BuildConfiguration *buildConfig = target->activeBuildConfiguration())
|
||||||
|
env = buildConfig->environment();
|
||||||
|
}
|
||||||
|
if (env.size() == 0)
|
||||||
|
env = Utils::Environment::systemEnvironment();
|
||||||
|
return env;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DocumentClangToolRunner::run()
|
||||||
|
{
|
||||||
|
cancel();
|
||||||
|
auto isEditorForCurrentDocument = [this](const Core::IEditor *editor) {
|
||||||
|
return editor->document() == m_document;
|
||||||
|
};
|
||||||
|
if (Utils::anyOf(Core::EditorManager::visibleEditors(), isEditorForCurrentDocument)) {
|
||||||
|
const Utils::FilePath filePath = m_document->filePath();
|
||||||
|
if (ProjectExplorer::Project *project = findProject(filePath)) {
|
||||||
|
m_fileInfo = getFileInfo(filePath, project);
|
||||||
|
if (m_fileInfo.file.exists()) {
|
||||||
|
const auto projectSettings = ClangToolsProjectSettings::getSettings(project);
|
||||||
|
|
||||||
|
const RunSettings &runSettings = projectSettings->useGlobalSettings()
|
||||||
|
? ClangToolsSettings::instance()->runSettings()
|
||||||
|
: projectSettings->runSettings();
|
||||||
|
m_projectSettingsUpdate = connect(projectSettings.data(),
|
||||||
|
&ClangToolsProjectSettings::changed,
|
||||||
|
this,
|
||||||
|
&DocumentClangToolRunner::run);
|
||||||
|
|
||||||
|
if (runSettings.analyzeOpenFiles()) {
|
||||||
|
vfso().update();
|
||||||
|
|
||||||
|
CppTools::ClangDiagnosticConfig config = diagnosticConfig(
|
||||||
|
runSettings.diagnosticConfigId());
|
||||||
|
|
||||||
|
Utils::Environment env = projectBuildEnvironment(project);
|
||||||
|
if (config.isClangTidyEnabled()) {
|
||||||
|
m_runnerCreators << [this, env, config]() {
|
||||||
|
return createRunner<ClangTidyRunner>(config, env);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (config.isClazyEnabled() && !m_document->isModified()) {
|
||||||
|
m_runnerCreators << [this, env, config]() {
|
||||||
|
return createRunner<ClazyStandaloneRunner>(config, env);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
runNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
QPair<Utils::FilePath, QString> getClangIncludeDirAndVersion(ClangToolRunner *runner)
|
||||||
|
{
|
||||||
|
static QMap<Utils::FilePath, QPair<Utils::FilePath, QString>> cache;
|
||||||
|
const Utils::FilePath tool = Utils::FilePath::fromString(runner->executable());
|
||||||
|
auto it = cache.find(tool);
|
||||||
|
if (it == cache.end())
|
||||||
|
it = cache.insert(tool, getClangIncludeDirAndVersion(tool));
|
||||||
|
return it.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DocumentClangToolRunner::runNext()
|
||||||
|
{
|
||||||
|
m_currentRunner.reset(m_runnerCreators.isEmpty() ? nullptr : m_runnerCreators.takeFirst()());
|
||||||
|
if (m_currentRunner) {
|
||||||
|
auto [clangIncludeDir, clangVersion] = getClangIncludeDirAndVersion(m_currentRunner.get());
|
||||||
|
qCDebug(LOG) << Q_FUNC_INFO << m_currentRunner->executable() << clangIncludeDir
|
||||||
|
<< clangVersion << m_fileInfo.file;
|
||||||
|
AnalyzeUnit unit(m_fileInfo, clangIncludeDir, clangVersion);
|
||||||
|
QTC_ASSERT(Utils::FilePath::fromString(unit.file).exists(), runNext(); return;);
|
||||||
|
m_currentRunner->setVFSOverlay(vfso().overlayFilePath().toString());
|
||||||
|
if (!m_currentRunner->run(unit.file, unit.arguments))
|
||||||
|
runNext();
|
||||||
|
} else {
|
||||||
|
finalize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DocumentClangToolRunner::onSuccess()
|
||||||
|
{
|
||||||
|
QString errorMessage;
|
||||||
|
Utils::FilePath mappedPath = vfso().filePath(m_document);
|
||||||
|
Diagnostics diagnostics = readExportedDiagnostics(
|
||||||
|
Utils::FilePath::fromString(m_currentRunner->outputFilePath()),
|
||||||
|
[&](const Utils::FilePath &path) { return path == mappedPath; },
|
||||||
|
&errorMessage);
|
||||||
|
|
||||||
|
if (mappedPath != m_document->filePath()) {
|
||||||
|
const QString originalPath = m_document->filePath().toString();
|
||||||
|
for (Diagnostic &diag : diagnostics)
|
||||||
|
diag.location.filePath = originalPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove outdated marks of the current runner
|
||||||
|
auto [toDelete, newMarks] = Utils::partition(m_marks, [this](DiagnosticMark *mark) {
|
||||||
|
return mark->source == m_currentRunner->name();
|
||||||
|
});
|
||||||
|
m_marks = newMarks;
|
||||||
|
qDeleteAll(toDelete);
|
||||||
|
|
||||||
|
m_marks << Utils::transform(diagnostics, [this](const Diagnostic &diagnostic) {
|
||||||
|
auto mark = new DiagnosticMark(diagnostic);
|
||||||
|
mark->source = m_currentRunner->name();
|
||||||
|
return mark;
|
||||||
|
});
|
||||||
|
runNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DocumentClangToolRunner::onFailure(const QString &errorMessage, const QString &errorDetails)
|
||||||
|
{
|
||||||
|
qCDebug(LOG) << "Failed to analyze " << m_fileInfo.file << ":" << errorMessage << errorDetails;
|
||||||
|
runNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DocumentClangToolRunner::finalize()
|
||||||
|
{
|
||||||
|
// remove all disabled textMarks
|
||||||
|
auto [newMarks, toDelete] = Utils::partition(m_marks, &DiagnosticMark::enabled);
|
||||||
|
m_marks = newMarks;
|
||||||
|
qDeleteAll(toDelete);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DocumentClangToolRunner::cancel()
|
||||||
|
{
|
||||||
|
if (m_projectSettingsUpdate)
|
||||||
|
disconnect(m_projectSettingsUpdate);
|
||||||
|
m_runnerCreators.clear();
|
||||||
|
if (m_currentRunner) {
|
||||||
|
m_currentRunner->disconnect(this);
|
||||||
|
m_currentRunner.reset(nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const CppTools::ClangDiagnosticConfig DocumentClangToolRunner::getDiagnosticConfig(ProjectExplorer::Project *project)
|
||||||
|
{
|
||||||
|
const auto projectSettings = ClangToolsProjectSettings::getSettings(project);
|
||||||
|
m_projectSettingsUpdate = connect(projectSettings.data(),
|
||||||
|
&ClangToolsProjectSettings::changed,
|
||||||
|
this,
|
||||||
|
&DocumentClangToolRunner::run);
|
||||||
|
|
||||||
|
const Utils::Id &id = projectSettings->useGlobalSettings()
|
||||||
|
? ClangToolsSettings::instance()->runSettings().diagnosticConfigId()
|
||||||
|
: projectSettings->runSettings().diagnosticConfigId();
|
||||||
|
return diagnosticConfig(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
ClangToolRunner *DocumentClangToolRunner::createRunner(const CppTools::ClangDiagnosticConfig &config,
|
||||||
|
const Utils::Environment &env)
|
||||||
|
{
|
||||||
|
auto runner = new T(config, this);
|
||||||
|
runner->init(m_temporaryDir.path(), env);
|
||||||
|
connect(runner, &ClangToolRunner::finishedWithSuccess,
|
||||||
|
this, &DocumentClangToolRunner::onSuccess);
|
||||||
|
connect(runner, &ClangToolRunner::finishedWithFailure,
|
||||||
|
this, &DocumentClangToolRunner::onFailure);
|
||||||
|
return runner;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace ClangTools
|
81
src/plugins/clangtools/documentclangtoolrunner.h
Normal file
81
src/plugins/clangtools/documentclangtoolrunner.h
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 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 "clangfileinfo.h"
|
||||||
|
|
||||||
|
#include <utils/fileutils.h>
|
||||||
|
#include <utils/temporarydirectory.h>
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
namespace Core { class IDocument; }
|
||||||
|
namespace CppTools { class ClangDiagnosticConfig; }
|
||||||
|
|
||||||
|
namespace ClangTools {
|
||||||
|
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
class ClangToolRunner;
|
||||||
|
class DiagnosticMark;
|
||||||
|
|
||||||
|
class DocumentClangToolRunner : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
DocumentClangToolRunner(Core::IDocument *doc);
|
||||||
|
~DocumentClangToolRunner();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void scheduleRun();
|
||||||
|
void run();
|
||||||
|
void runNext();
|
||||||
|
|
||||||
|
void onSuccess();
|
||||||
|
void onFailure(const QString &errorMessage, const QString &errorDetails);
|
||||||
|
|
||||||
|
void finalize();
|
||||||
|
|
||||||
|
void cancel();
|
||||||
|
|
||||||
|
const CppTools::ClangDiagnosticConfig getDiagnosticConfig(ProjectExplorer::Project *project);
|
||||||
|
template<class T>
|
||||||
|
ClangToolRunner *createRunner(const CppTools::ClangDiagnosticConfig &config,
|
||||||
|
const Utils::Environment &env);
|
||||||
|
|
||||||
|
QTimer m_runTimer;
|
||||||
|
Core::IDocument *m_document = nullptr;
|
||||||
|
Utils::TemporaryDirectory m_temporaryDir;
|
||||||
|
std::unique_ptr<ClangToolRunner> m_currentRunner;
|
||||||
|
QList<std::function<ClangToolRunner *()>> m_runnerCreators;
|
||||||
|
QList<DiagnosticMark *> m_marks;
|
||||||
|
FileInfo m_fileInfo;
|
||||||
|
QMetaObject::Connection m_projectSettingsUpdate;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace ClangTools
|
@@ -110,6 +110,9 @@ void RunSettingsWidget::fromSettings(const RunSettings &s)
|
|||||||
connect(m_ui->parallelJobsSpinBox,
|
connect(m_ui->parallelJobsSpinBox,
|
||||||
QOverload<int>::of(&QSpinBox::valueChanged),
|
QOverload<int>::of(&QSpinBox::valueChanged),
|
||||||
[this](int) { emit changed(); });
|
[this](int) { emit changed(); });
|
||||||
|
m_ui->analyzeOpenFiles->setChecked(s.analyzeOpenFiles());
|
||||||
|
connect(m_ui->analyzeOpenFiles, &QCheckBox::toggled, this, &RunSettingsWidget::changed);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RunSettings RunSettingsWidget::toSettings() const
|
RunSettings RunSettingsWidget::toSettings() const
|
||||||
@@ -118,6 +121,7 @@ RunSettings RunSettingsWidget::toSettings() const
|
|||||||
s.setDiagnosticConfigId(m_ui->diagnosticWidget->currentConfigId());
|
s.setDiagnosticConfigId(m_ui->diagnosticWidget->currentConfigId());
|
||||||
s.setBuildBeforeAnalysis(m_ui->buildBeforeAnalysis->checkState() == Qt::CheckState::Checked);
|
s.setBuildBeforeAnalysis(m_ui->buildBeforeAnalysis->checkState() == Qt::CheckState::Checked);
|
||||||
s.setParallelJobs(m_ui->parallelJobsSpinBox->value());
|
s.setParallelJobs(m_ui->parallelJobsSpinBox->value());
|
||||||
|
s.setAnalyzeOpenFiles(m_ui->analyzeOpenFiles->checkState() == Qt::CheckState::Checked);
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
@@ -42,6 +42,13 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="analyzeOpenFiles">
|
||||||
|
<property name="text">
|
||||||
|
<string>Analyze open files</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="processesLayout">
|
<layout class="QHBoxLayout" name="processesLayout">
|
||||||
<item>
|
<item>
|
||||||
|
124
src/plugins/clangtools/virtualfilesystemoverlay.cpp
Normal file
124
src/plugins/clangtools/virtualfilesystemoverlay.cpp
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 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 "virtualfilesystemoverlay.h"
|
||||||
|
|
||||||
|
#include <coreplugin/documentmanager.h>
|
||||||
|
#include <texteditor/textdocument.h>
|
||||||
|
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
#include <QTextDocument>
|
||||||
|
|
||||||
|
static Q_LOGGING_CATEGORY(LOG, "qtc.clangtools.vfso", QtWarningMsg)
|
||||||
|
|
||||||
|
namespace ClangTools {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
VirtualFileSystemOverlay::VirtualFileSystemOverlay(const QString &rootPattern)
|
||||||
|
: m_root(rootPattern)
|
||||||
|
, m_overlayFilePath(Utils::FilePath::fromString(m_root.filePath("vfso.yaml")))
|
||||||
|
{ }
|
||||||
|
|
||||||
|
void VirtualFileSystemOverlay::update()
|
||||||
|
{
|
||||||
|
Utils::FileUtils::removeRecursively(overlayFilePath());
|
||||||
|
QFile overlayFile(m_overlayFilePath.toString());
|
||||||
|
if (!overlayFile.open(QFile::ReadWrite))
|
||||||
|
return;
|
||||||
|
std::map<Utils::FilePath, QList<Core::IDocument *>> documentRoots;
|
||||||
|
const QList<Core::IDocument *> &modifiedDocuments = Core::DocumentManager::modifiedDocuments();
|
||||||
|
QMap<Core::IDocument *, AutoSavedPath> newSaved;
|
||||||
|
for (Core::IDocument *doc : modifiedDocuments) {
|
||||||
|
auto document = qobject_cast<TextEditor::TextDocument *>(doc);
|
||||||
|
if (!document)
|
||||||
|
continue;
|
||||||
|
documentRoots[doc->filePath().absolutePath()] << doc;
|
||||||
|
AutoSavedPath saved = m_saved.take(document);
|
||||||
|
if (saved.revision != document->document()->revision()) {
|
||||||
|
saved.revision = document->document()->revision();
|
||||||
|
QString error;
|
||||||
|
saved.path = Utils::FilePath::fromString(m_root.path())
|
||||||
|
.pathAppended(doc->filePath().fileName() + ".auto");
|
||||||
|
while (saved.path.exists())
|
||||||
|
saved.path = saved.path + ".1";
|
||||||
|
if (!doc->save(&error, saved.path.toString(), true)) {
|
||||||
|
qCDebug(LOG) << error;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newSaved[doc] = saved;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const AutoSavedPath &path : qAsConst(m_saved)) {
|
||||||
|
QString error;
|
||||||
|
if (!Utils::FileUtils::removeRecursively(path.path, &error))
|
||||||
|
qCDebug(LOG) << error;
|
||||||
|
}
|
||||||
|
m_saved = newSaved;
|
||||||
|
|
||||||
|
auto toContent = [this](Core::IDocument *document) {
|
||||||
|
QJsonObject content;
|
||||||
|
content["name"] = document->filePath().fileName();
|
||||||
|
content["type"] = "file";
|
||||||
|
content["external-contents"] = m_saved[document].path.toUserOutput();
|
||||||
|
return content;
|
||||||
|
};
|
||||||
|
|
||||||
|
QJsonObject main;
|
||||||
|
main["version"] = 0;
|
||||||
|
QJsonArray jsonRoots;
|
||||||
|
for (auto [root, documents] : documentRoots) {
|
||||||
|
QJsonObject jsonRoot;
|
||||||
|
jsonRoot["type"] = "directory";
|
||||||
|
jsonRoot["name"] = root.toUserOutput();
|
||||||
|
QJsonArray contents;
|
||||||
|
for (auto doc : documents)
|
||||||
|
contents << toContent(doc);
|
||||||
|
jsonRoot["contents"] = contents;
|
||||||
|
jsonRoots << jsonRoot;
|
||||||
|
}
|
||||||
|
main["roots"] = jsonRoots;
|
||||||
|
|
||||||
|
QJsonDocument overlay(main);
|
||||||
|
if (!overlayFile.write(overlay.toJson(QJsonDocument::Compact)))
|
||||||
|
qCDebug(LOG) << "failed to write vfso to " << m_overlayFilePath;
|
||||||
|
overlayFile.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
Utils::FilePath VirtualFileSystemOverlay::overlayFilePath() { return m_overlayFilePath; }
|
||||||
|
|
||||||
|
Utils::FilePath VirtualFileSystemOverlay::filePath(Core::IDocument *doc)
|
||||||
|
{
|
||||||
|
auto it = m_saved.find(doc);
|
||||||
|
if (it != m_saved.end())
|
||||||
|
return it.value().path;
|
||||||
|
return doc->filePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace ClangTools
|
59
src/plugins/clangtools/virtualfilesystemoverlay.h
Normal file
59
src/plugins/clangtools/virtualfilesystemoverlay.h
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 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 <utils/fileutils.h>
|
||||||
|
#include <utils/temporarydirectory.h>
|
||||||
|
|
||||||
|
namespace Core { class IDocument; }
|
||||||
|
|
||||||
|
namespace ClangTools {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
class VirtualFileSystemOverlay
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VirtualFileSystemOverlay(const QString &rootPattern);
|
||||||
|
|
||||||
|
void update();
|
||||||
|
|
||||||
|
Utils::FilePath overlayFilePath();
|
||||||
|
Utils::FilePath filePath(Core::IDocument *doc);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Utils::TemporaryDirectory m_root;
|
||||||
|
Utils::FilePath m_overlayFilePath;
|
||||||
|
struct AutoSavedPath
|
||||||
|
{
|
||||||
|
int revision = -1;
|
||||||
|
Utils::FilePath path;
|
||||||
|
};
|
||||||
|
|
||||||
|
QMap<Core::IDocument *, AutoSavedPath> m_saved;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace ClangTools
|
Reference in New Issue
Block a user