diff options
author | David Schulz <david.schulz@qt.io> | 2020-07-22 14:52:06 +0200 |
---|---|---|
committer | David Schulz <david.schulz@qt.io> | 2020-08-28 07:20:32 +0000 |
commit | e176958da112984a634b355142cdd5d59a748efc (patch) | |
tree | 388d14991d0bc58ddbf4d6bfe7bd528d95869caa /src | |
parent | 7f562c4d33a042fe51946c273581b2e9ad64159f (diff) |
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>
Diffstat (limited to 'src')
24 files changed, 781 insertions, 79 deletions
diff --git a/src/plugins/clangtools/CMakeLists.txt b/src/plugins/clangtools/CMakeLists.txt index 4ac94e7c25..829b77c97a 100644 --- a/src/plugins/clangtools/CMakeLists.txt +++ b/src/plugins/clangtools/CMakeLists.txt @@ -32,11 +32,13 @@ add_qtc_plugin(ClangTools clazychecks.ui diagnosticconfigswidget.cpp diagnosticconfigswidget.h diagnosticmark.cpp diagnosticmark.h + documentclangtoolrunner.cpp documentclangtoolrunner.h executableinfo.cpp executableinfo.h filterdialog.cpp filterdialog.h filterdialog.ui runsettingswidget.cpp runsettingswidget.h runsettingswidget.ui settingswidget.cpp settingswidget.h settingswidget.ui tidychecks.ui + virtualfilesystemoverlay.cpp virtualfilesystemoverlay.h ) extend_qtc_plugin(ClangTools diff --git a/src/plugins/clangtools/clangtidyclazyrunner.cpp b/src/plugins/clangtools/clangtidyclazyrunner.cpp index 8fbc09ff5a..d3e2b34198 100644 --- a/src/plugins/clangtools/clangtidyclazyrunner.cpp +++ b/src/plugins/clangtools/clangtidyclazyrunner.cpp @@ -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, const QStringList &baseOptions) { @@ -102,11 +109,11 @@ ClangTidyRunner::ClangTidyRunner(const ClangDiagnosticConfig &config, QObject *p setOutputFileFormat(OutputFileFormat::Yaml); setExecutable(clangTidyExecutable()); setArgsCreator([this, config](const QStringList &baseOptions) { - return QStringList() - << tidyChecksArguments(config) - << mainToolArguments(fileToAnalyze(), outputFilePath()) - << "--" - << clangArguments(config, baseOptions); + return QStringList() << tidyChecksArguments(config) + << mainToolArguments(fileToAnalyze(), outputFilePath()) + << virtualFileSystemOverlay(m_overlayFilePath) + << "--" + << clangArguments(config, baseOptions); }); } diff --git a/src/plugins/clangtools/clangtool.cpp b/src/plugins/clangtools/clangtool.cpp index 49f389b4f2..fde54fe130 100644 --- a/src/plugins/clangtools/clangtool.cpp +++ b/src/plugins/clangtools/clangtool.cpp @@ -376,13 +376,6 @@ static RunSettings 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() { return s_instance; diff --git a/src/plugins/clangtools/clangtoolruncontrol.cpp b/src/plugins/clangtools/clangtoolruncontrol.cpp index ac054135de..738e98fa6c 100644 --- a/src/plugins/clangtools/clangtoolruncontrol.cpp +++ b/src/plugins/clangtools/clangtoolruncontrol.cpp @@ -73,45 +73,6 @@ using namespace Utils; 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 Internal { @@ -154,26 +115,21 @@ private: bool m_success = false; }; -static AnalyzeUnits toAnalyzeUnits(const FileInfos &fileInfos, const FilePath &clangIncludeDir, - const QString &clangVersion) +AnalyzeUnit::AnalyzeUnit(const FileInfo &fileInfo, + const FilePath &clangIncludeDir, + const QString &clangVersion) { - AnalyzeUnits unitsToAnalyze; - const UsePrecompiledHeaders usePrecompiledHeaders = CppTools::getPchUsage(); - for (const FileInfo &fileInfo : fileInfos) { - CompilerOptionsBuilder optionsBuilder(*fileInfo.projectPart, - UseSystemHeader::No, - UseTweakedHeaderPaths::Yes, - UseLanguageDefines::No, - UseBuildSystemWarnings::No, - clangVersion, - clangIncludeDir.toString()); - QStringList arguments = extraClangToolsPrependOptions(); - arguments.append(optionsBuilder.build(fileInfo.kind, usePrecompiledHeaders)); - arguments.append(extraClangToolsAppendOptions()); - unitsToAnalyze << AnalyzeUnit(fileInfo.file.toString(), arguments); - } - - return unitsToAnalyze; + CompilerOptionsBuilder optionsBuilder(*fileInfo.projectPart, + UseSystemHeader::No, + UseTweakedHeaderPaths::Yes, + UseLanguageDefines::No, + UseBuildSystemWarnings::No, + clangVersion, + clangIncludeDir.toString()); + file = fileInfo.file.toString(); + arguments = extraClangToolsPrependOptions(); + arguments.append(optionsBuilder.build(fileInfo.kind, CppTools::getPchUsage())); + arguments.append(extraClangToolsAppendOptions()); } AnalyzeUnits ClangToolRunWorker::unitsToAnalyze(const FilePath &clangIncludeDir, @@ -181,7 +137,10 @@ AnalyzeUnits ClangToolRunWorker::unitsToAnalyze(const FilePath &clangIncludeDir, { 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) @@ -292,6 +251,8 @@ void ClangToolRunWorker::start() getClangIncludeDirAndVersion(runControl()->runnable().executable); const AnalyzeUnits unitsToProcess = unitsToAnalyze(clangIncludeDirAndVersion.first, clangIncludeDirAndVersion.second); + qCDebug(LOG) << Q_FUNC_INFO << runControl()->runnable().executable + << clangIncludeDirAndVersion.first << clangIncludeDirAndVersion.second; qCDebug(LOG) << "Files to process:" << unitsToProcess; m_queue.clear(); diff --git a/src/plugins/clangtools/clangtoolruncontrol.h b/src/plugins/clangtools/clangtoolruncontrol.h index e72df21c68..9f46d0f2e3 100644 --- a/src/plugins/clangtools/clangtoolruncontrol.h +++ b/src/plugins/clangtools/clangtoolruncontrol.h @@ -46,8 +46,9 @@ class ClangToolRunner; class ProjectBuilder; struct AnalyzeUnit { - AnalyzeUnit(const QString &file, const QStringList &options) - : file(file), arguments(options) {} + AnalyzeUnit(const FileInfo &fileInfo, + const Utils::FilePath &clangResourceDir, + const QString &clangVersion); QString file; QStringList arguments; // without file itself and "-o somePath" diff --git a/src/plugins/clangtools/clangtoolrunner.cpp b/src/plugins/clangtools/clangtoolrunner.cpp index 0a63e8731b..b0fc220e04 100644 --- a/src/plugins/clangtools/clangtoolrunner.cpp +++ b/src/plugins/clangtools/clangtoolrunner.cpp @@ -75,7 +75,15 @@ void ClangToolRunner::init(const QString &outputDirPath, 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) diff --git a/src/plugins/clangtools/clangtoolrunner.h b/src/plugins/clangtools/clangtoolrunner.h index 575876ff6c..87ec5d5f06 100644 --- a/src/plugins/clangtools/clangtoolrunner.h +++ b/src/plugins/clangtools/clangtoolrunner.h @@ -52,6 +52,7 @@ public: void setExecutable(const QString &executable) { m_executable = executable; } void setArgsCreator(const ArgsCreator &argsCreator) { m_argsCreator = argsCreator; } void setOutputFileFormat(const OutputFileFormat &format) { m_outputFileFormat = format; } + void setVFSOverlay(const QString overlayFilePath) { m_overlayFilePath = overlayFilePath; } QString name() const { return m_name; } QString executable() const { return m_executable; } @@ -68,6 +69,9 @@ signals: void finishedWithSuccess(const QString &fileToAnalyze); void finishedWithFailure(const QString &errorMessage, const QString &errorDetails); +protected: + QString m_overlayFilePath; + private: void onProcessOutput(); void onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus); diff --git a/src/plugins/clangtools/clangtools.pro b/src/plugins/clangtools/clangtools.pro index 2d2c3b3c27..f4a85c24e6 100644 --- a/src/plugins/clangtools/clangtools.pro +++ b/src/plugins/clangtools/clangtools.pro @@ -30,10 +30,12 @@ SOURCES += \ clangtoolsutils.cpp \ diagnosticconfigswidget.cpp \ diagnosticmark.cpp \ + documentclangtoolrunner.cpp \ executableinfo.cpp \ filterdialog.cpp \ runsettingswidget.cpp \ settingswidget.cpp \ + virtualfilesystemoverlay.cpp \ HEADERS += \ clangfileinfo.h \ @@ -56,10 +58,12 @@ HEADERS += \ clangtoolsutils.h \ diagnosticconfigswidget.h \ diagnosticmark.h \ + documentclangtoolrunner.h \ executableinfo.h \ filterdialog.h \ runsettingswidget.h \ settingswidget.h \ + virtualfilesystemoverlay.h \ FORMS += \ clangselectablefilesdialog.ui \ diff --git a/src/plugins/clangtools/clangtools.qbs b/src/plugins/clangtools/clangtools.qbs index eab5cd240c..6e9d83b14d 100644 --- a/src/plugins/clangtools/clangtools.qbs +++ b/src/plugins/clangtools/clangtools.qbs @@ -68,6 +68,8 @@ QtcPlugin { "diagnosticconfigswidget.h", "diagnosticmark.cpp", "diagnosticmark.h", + "documentclangtoolrunner.cpp", + "documentclangtoolrunner.h", "executableinfo.cpp", "executableinfo.h", "filterdialog.cpp", @@ -80,6 +82,8 @@ QtcPlugin { "settingswidget.h", "settingswidget.ui", "tidychecks.ui", + "virtualfilesystemoverlay.cpp", + "virtualfilesystemoverlay.h", ] Group { diff --git a/src/plugins/clangtools/clangtoolsplugin.cpp b/src/plugins/clangtools/clangtoolsplugin.cpp index 242575d5fc..63f5df80cc 100644 --- a/src/plugins/clangtools/clangtoolsplugin.cpp +++ b/src/plugins/clangtools/clangtoolsplugin.cpp @@ -29,6 +29,7 @@ #include "clangtoolsconstants.h" #include "clangtoolsprojectsettings.h" #include "clangtoolsprojectsettingswidget.h" +#include "documentclangtoolrunner.h" #include "settingswidget.h" #ifdef WITH_TESTS @@ -84,6 +85,7 @@ class ClangToolsPluginPrivate public: ClangTool clangTool; ClangToolsOptionsPage optionsPage; + QMap<Core::IDocument *, DocumentClangToolRunner *> documentRunners; }; ClangToolsPlugin::~ClangToolsPlugin() @@ -111,9 +113,28 @@ bool ClangToolsPlugin::initialize(const QStringList &arguments, QString *errorSt panelFactory->setCreateWidgetFunction([](Project *project) { return new ProjectSettingsWidget(project); }); ProjectPanelFactory::registerFactory(panelFactory); + connect(Core::EditorManager::instance(), + &Core::EditorManager::currentEditorChanged, + this, + &ClangToolsPlugin::onCurrentEditorChanged); + 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() { ActionManager::registerAction(d->clangTool.startAction(), Constants::RUN_ON_PROJECT); diff --git a/src/plugins/clangtools/clangtoolsplugin.h b/src/plugins/clangtools/clangtoolsplugin.h index aaf1711fd6..fb6d4b279c 100644 --- a/src/plugins/clangtools/clangtoolsplugin.h +++ b/src/plugins/clangtools/clangtoolsplugin.h @@ -27,6 +27,7 @@ #include <extensionsystem/iplugin.h> +namespace Core { class IDocument; } namespace ProjectExplorer { class ProjectPanelFactory; } namespace ClangTools { @@ -46,6 +47,7 @@ public: private: bool initialize(const QStringList &arguments, QString *errorString) final; void registerAnalyzeActions(); + void onCurrentEditorChanged(); QVector<QObject *> createTestObjects() const final; diff --git a/src/plugins/clangtools/clangtoolsprojectsettings.cpp b/src/plugins/clangtools/clangtoolsprojectsettings.cpp index e83c3b36f1..e92d87771b 100644 --- a/src/plugins/clangtools/clangtoolsprojectsettings.cpp +++ b/src/plugins/clangtools/clangtoolsprojectsettings.cpp @@ -48,6 +48,8 @@ ClangToolsProjectSettings::ClangToolsProjectSettings(ProjectExplorer::Project *p : m_project(project) { load(); + connect(this, &ClangToolsProjectSettings::suppressedDiagnosticsChanged, + this, &ClangToolsProjectSettings::changed); connect(project, &ProjectExplorer::Project::settingsLoaded, this, &ClangToolsProjectSettings::load); connect(project, &ProjectExplorer::Project::aboutToSaveSettings, this, @@ -59,6 +61,38 @@ ClangToolsProjectSettings::~ClangToolsProjectSettings() 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) { QTC_ASSERT(!m_suppressedDiagnostics.contains(diag), return); diff --git a/src/plugins/clangtools/clangtoolsprojectsettings.h b/src/plugins/clangtools/clangtoolsprojectsettings.h index aa6c035395..ed3c57fd72 100644 --- a/src/plugins/clangtools/clangtoolsprojectsettings.h +++ b/src/plugins/clangtools/clangtoolsprojectsettings.h @@ -70,16 +70,16 @@ public: ~ClangToolsProjectSettings() override; bool useGlobalSettings() const { return m_useGlobalSettings; } - void setUseGlobalSettings(bool useGlobalSettings) { m_useGlobalSettings = useGlobalSettings; } + void setUseGlobalSettings(bool useGlobalSettings); 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; } - 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; } - void setSelectedFiles(const QSet<Utils::FilePath> &value) { m_selectedFiles = value; } + void setSelectedFiles(const QSet<Utils::FilePath> &value); SuppressedDiagnosticsList suppressedDiagnostics() const { return m_suppressedDiagnostics; } void addSuppressedDiagnostic(const SuppressedDiagnostic &diag); @@ -91,6 +91,7 @@ public: signals: void suppressedDiagnosticsChanged(); + void changed(); private: void load(); diff --git a/src/plugins/clangtools/clangtoolssettings.cpp b/src/plugins/clangtools/clangtoolssettings.cpp index ec76cdd729..2342e12b1d 100644 --- a/src/plugins/clangtools/clangtoolssettings.cpp +++ b/src/plugins/clangtools/clangtoolssettings.cpp @@ -42,7 +42,7 @@ static const char clazyStandaloneExecutableKey[] = "ClazyStandaloneExecutable"; static const char parallelJobsKey[] = "ParallelJobs"; static const char buildBeforeAnalysisKey[] = "BuildBeforeAnalysis"; - +static const char analyzeOpenFilesKey[] = "AnalyzeOpenFiles"; static const char oldDiagnosticConfigIdKey[] = "diagnosticConfigId"; 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_parallelJobs = map.value(prefix + parallelJobsKey).toInt(); m_buildBeforeAnalysis = map.value(prefix + buildBeforeAnalysisKey).toBool(); + m_analyzeOpenFiles = map.value(prefix + analyzeOpenFilesKey).toBool(); } 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 + parallelJobsKey, m_parallelJobs); map.insert(prefix + buildBeforeAnalysisKey, m_buildBeforeAnalysis); + map.insert(prefix + analyzeOpenFilesKey, m_analyzeOpenFiles); } Utils::Id RunSettings::diagnosticConfigId() const @@ -82,6 +84,14 @@ Utils::Id RunSettings::diagnosticConfigId() const 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() { readSettings(); @@ -154,6 +164,7 @@ void ClangToolsSettings::readSettings() defaults.insert(diagnosticConfigIdKey, defaultDiagnosticId().toSetting()); defaults.insert(parallelJobsKey, m_runSettings.parallelJobs()); defaults.insert(buildBeforeAnalysisKey, m_runSettings.buildBeforeAnalysis()); + defaults.insert(analyzeOpenFilesKey, m_runSettings.analyzeOpenFiles()); map = defaults; for (QVariantMap::ConstIterator it = defaults.constBegin(); it != defaults.constEnd(); ++it) map.insert(it.key(), s->value(it.key(), it.value())); diff --git a/src/plugins/clangtools/clangtoolssettings.h b/src/plugins/clangtools/clangtoolssettings.h index 21b62e02a4..ac2a6b4185 100644 --- a/src/plugins/clangtools/clangtoolssettings.h +++ b/src/plugins/clangtools/clangtoolssettings.h @@ -54,10 +54,16 @@ public: int parallelJobs() const { return m_parallelJobs; } 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: Utils::Id m_diagnosticConfigId; int m_parallelJobs = -1; bool m_buildBeforeAnalysis = true; + bool m_analyzeOpenFiles = true; }; class ClangToolsSettings : public QObject diff --git a/src/plugins/clangtools/clangtoolsutils.cpp b/src/plugins/clangtools/clangtoolsutils.cpp index 6c394aebc0..99cb1c9dd3 100644 --- a/src/plugins/clangtools/clangtoolsutils.cpp +++ b/src/plugins/clangtools/clangtoolsutils.cpp @@ -35,6 +35,7 @@ #include <cpptools/cpptoolsreuse.h> #include <projectexplorer/projectexplorerconstants.h> +#include <utils/qtcprocess.h> #include <utils/checkablemessagebox.h> #include <utils/environment.h> #include <utils/hostosinfo.h> @@ -304,5 +305,51 @@ QString documentationUrl(const QString &checkName) 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 ClangTools diff --git a/src/plugins/clangtools/clangtoolsutils.h b/src/plugins/clangtools/clangtoolsutils.h index 4aa6f7eac9..9b6ddfa4c2 100644 --- a/src/plugins/clangtools/clangtoolsutils.h +++ b/src/plugins/clangtools/clangtoolsutils.h @@ -80,5 +80,10 @@ CppTools::ClangDiagnosticConfigsModel diagnosticConfigsModel(); CppTools::ClangDiagnosticConfigsModel diagnosticConfigsModel( const CppTools::ClangDiagnosticConfigs &customConfigs); +CppTools::ClangDiagnosticConfig diagnosticConfig(const Utils::Id &diagConfigId); + +QStringList extraClangToolsPrependOptions(); +QStringList extraClangToolsAppendOptions(); + } // namespace Internal } // namespace ClangTools diff --git a/src/plugins/clangtools/diagnosticmark.h b/src/plugins/clangtools/diagnosticmark.h index 040ce16adc..38cbe281a2 100644 --- a/src/plugins/clangtools/diagnosticmark.h +++ b/src/plugins/clangtools/diagnosticmark.h @@ -41,6 +41,8 @@ public: void disable(); bool enabled() const; + QString source; + private: const Diagnostic m_diagnostic; bool m_enabled = true; diff --git a/src/plugins/clangtools/documentclangtoolrunner.cpp b/src/plugins/clangtools/documentclangtoolrunner.cpp new file mode 100644 index 0000000000..b37c39087d --- /dev/null +++ b/src/plugins/clangtools/documentclangtoolrunner.cpp @@ -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 diff --git a/src/plugins/clangtools/documentclangtoolrunner.h b/src/plugins/clangtools/documentclangtoolrunner.h new file mode 100644 index 0000000000..4cf263c1aa --- /dev/null +++ b/src/plugins/clangtools/documentclangtoolrunner.h @@ -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 diff --git a/src/plugins/clangtools/runsettingswidget.cpp b/src/plugins/clangtools/runsettingswidget.cpp index 76fc820373..64fd25836e 100644 --- a/src/plugins/clangtools/runsettingswidget.cpp +++ b/src/plugins/clangtools/runsettingswidget.cpp @@ -110,6 +110,9 @@ void RunSettingsWidget::fromSettings(const RunSettings &s) connect(m_ui->parallelJobsSpinBox, QOverload<int>::of(&QSpinBox::valueChanged), [this](int) { emit changed(); }); + m_ui->analyzeOpenFiles->setChecked(s.analyzeOpenFiles()); + connect(m_ui->analyzeOpenFiles, &QCheckBox::toggled, this, &RunSettingsWidget::changed); + } RunSettings RunSettingsWidget::toSettings() const @@ -118,6 +121,7 @@ RunSettings RunSettingsWidget::toSettings() const s.setDiagnosticConfigId(m_ui->diagnosticWidget->currentConfigId()); s.setBuildBeforeAnalysis(m_ui->buildBeforeAnalysis->checkState() == Qt::CheckState::Checked); s.setParallelJobs(m_ui->parallelJobsSpinBox->value()); + s.setAnalyzeOpenFiles(m_ui->analyzeOpenFiles->checkState() == Qt::CheckState::Checked); return s; } diff --git a/src/plugins/clangtools/runsettingswidget.ui b/src/plugins/clangtools/runsettingswidget.ui index e7ad362c84..370932c9a8 100644 --- a/src/plugins/clangtools/runsettingswidget.ui +++ b/src/plugins/clangtools/runsettingswidget.ui @@ -43,6 +43,13 @@ </widget> </item> <item> + <widget class="QCheckBox" name="analyzeOpenFiles"> + <property name="text"> + <string>Analyze open files</string> + </property> + </widget> + </item> + <item> <layout class="QHBoxLayout" name="processesLayout"> <item> <widget class="QLabel" name="label_2"> diff --git a/src/plugins/clangtools/virtualfilesystemoverlay.cpp b/src/plugins/clangtools/virtualfilesystemoverlay.cpp new file mode 100644 index 0000000000..a0f9f2725c --- /dev/null +++ b/src/plugins/clangtools/virtualfilesystemoverlay.cpp @@ -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 diff --git a/src/plugins/clangtools/virtualfilesystemoverlay.h b/src/plugins/clangtools/virtualfilesystemoverlay.h new file mode 100644 index 0000000000..f6077f12a5 --- /dev/null +++ b/src/plugins/clangtools/virtualfilesystemoverlay.h @@ -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 |