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:
David Schulz
2020-07-22 14:52:06 +02:00
parent 7f562c4d33
commit e176958da1
24 changed files with 781 additions and 79 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View 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

View File

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

View File

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

View 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

View 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