forked from qt-creator/qt-creator
ClangTools: Use compilation database when running clang-tidy and clazy
Fixes: QTCREATORBUG-29529 Change-Id: I917c53352e448dfd9e58e7a6e4dabb71e8f79491 Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
@@ -21,6 +21,7 @@ add_qtc_plugin(ClangTools
|
||||
clangtool.cpp clangtool.h
|
||||
clangtoolrunner.cpp clangtoolrunner.h
|
||||
clangtools_global.h
|
||||
clangtoolscompilationdb.cpp clangtoolscompilationdb.h
|
||||
clangtoolstr.h
|
||||
clangtoolsconstants.h
|
||||
clangtoolsdiagnostic.cpp clangtoolsdiagnostic.h
|
||||
|
@@ -5,6 +5,7 @@
|
||||
|
||||
#include "clangselectablefilesdialog.h"
|
||||
#include "clangtoolrunner.h"
|
||||
#include "clangtoolscompilationdb.h"
|
||||
#include "clangtoolsconstants.h"
|
||||
#include "clangtoolsdiagnosticview.h"
|
||||
#include "clangtoolsprojectsettings.h"
|
||||
@@ -753,7 +754,7 @@ Group ClangTool::runRecipe(const RunSettings &runSettings,
|
||||
for (const FileInfo &fileInfo : fileInfos) {
|
||||
if (diagnosticConfig.isEnabled(tool)
|
||||
|| runSettings.hasConfigFileForSourceFile(fileInfo.file)) {
|
||||
unitsToProcess.append({fileInfo, includeDir, clangVersion});
|
||||
unitsToProcess.append({fileInfo, tool});
|
||||
}
|
||||
}
|
||||
qCDebug(LOG) << Q_FUNC_INFO << executable << includeDir << clangVersion;
|
||||
@@ -798,7 +799,8 @@ Group ClangTool::runRecipe(const RunSettings &runSettings,
|
||||
const AnalyzeInputData input{tool, runSettings, diagnosticConfig, tempDir->path(),
|
||||
environment};
|
||||
|
||||
taskTree.setRecipe({clangToolTask(unitsToProcess, input, setupHandler, outputHandler)});
|
||||
taskTree.setRecipe(
|
||||
{clangToolTask(tool, unitsToProcess, input, setupHandler, outputHandler)});
|
||||
return SetupResult::Continue;
|
||||
};
|
||||
|
||||
@@ -830,6 +832,9 @@ Group ClangTool::runRecipe(const RunSettings &runSettings,
|
||||
void ClangTool::startTool(FileSelection fileSelection, const RunSettings &runSettings,
|
||||
const ClangDiagnosticConfig &diagnosticConfig)
|
||||
{
|
||||
ClangToolsCompilationDb &db = ClangToolsCompilationDb::getDb(m_type);
|
||||
db.disconnect(this);
|
||||
|
||||
Project *project = ProjectManager::startupProject();
|
||||
QTC_ASSERT(project, return);
|
||||
QTC_ASSERT(project->activeTarget(), return);
|
||||
@@ -841,6 +846,14 @@ void ClangTool::startTool(FileSelection fileSelection, const RunSettings &runSet
|
||||
return;
|
||||
}
|
||||
|
||||
if (db.generateIfNecessary()) {
|
||||
connect(&db, &ClangToolsCompilationDb::generated, this, [=, this](bool success) {
|
||||
if (success)
|
||||
startTool(fileSelection, runSettings, diagnosticConfig);
|
||||
}, Qt::SingleShotConnection);
|
||||
return;
|
||||
}
|
||||
|
||||
TaskHub::clearTasks(taskCategory());
|
||||
|
||||
// Collect files to analyze
|
||||
|
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "clangtoolrunner.h"
|
||||
|
||||
#include "clangtoolscompilationdb.h"
|
||||
#include "clangtoolslogfilereader.h"
|
||||
#include "clangtoolstr.h"
|
||||
#include "clangtoolsutils.h"
|
||||
@@ -34,31 +35,6 @@ using namespace Tasking;
|
||||
namespace ClangTools {
|
||||
namespace Internal {
|
||||
|
||||
AnalyzeUnit::AnalyzeUnit(const FileInfo &fileInfo,
|
||||
const FilePath &clangIncludeDir,
|
||||
const QString &clangVersion)
|
||||
{
|
||||
const FilePath actualClangIncludeDir = Core::ICore::clangIncludeDirectory(
|
||||
clangVersion, clangIncludeDir);
|
||||
CompilerOptionsBuilder optionsBuilder(*fileInfo.projectPart,
|
||||
UseSystemHeader::No,
|
||||
UseTweakedHeaderPaths::Tools,
|
||||
UseLanguageDefines::No,
|
||||
UseBuildSystemWarnings::No,
|
||||
actualClangIncludeDir);
|
||||
file = fileInfo.file;
|
||||
arguments = extraClangToolsPrependOptions();
|
||||
arguments.append(
|
||||
optionsBuilder.build(fileInfo.kind,
|
||||
CppCodeModelSettings(fileInfo.settings).usePrecompiledHeaders()));
|
||||
arguments.append(extraClangToolsAppendOptions());
|
||||
}
|
||||
|
||||
static bool isClMode(const QStringList &options)
|
||||
{
|
||||
return options.contains("--driver-mode=cl");
|
||||
}
|
||||
|
||||
static QStringList checksArguments(const AnalyzeUnit &unit, const AnalyzeInputData &input)
|
||||
{
|
||||
if (input.tool == ClangToolType::Tidy) {
|
||||
@@ -78,24 +54,6 @@ static QStringList checksArguments(const AnalyzeUnit &unit, const AnalyzeInputDa
|
||||
return {};
|
||||
}
|
||||
|
||||
static QStringList clangArguments(const AnalyzeUnit &unit, const AnalyzeInputData &input)
|
||||
{
|
||||
QStringList arguments;
|
||||
const ClangDiagnosticConfig &diagnosticConfig = input.config;
|
||||
const QStringList &baseOptions = unit.arguments;
|
||||
arguments << ClangDiagnosticConfigsModel::globalDiagnosticOptions()
|
||||
<< (isClMode(baseOptions) ? clangArgsForCl(diagnosticConfig.clangOptions())
|
||||
: diagnosticConfig.clangOptions())
|
||||
<< baseOptions;
|
||||
if (ProjectFile::isHeader(unit.file))
|
||||
arguments << "-Wno-pragma-once-outside-header";
|
||||
|
||||
if (LOG().isDebugEnabled())
|
||||
arguments << QLatin1String("-v");
|
||||
|
||||
return arguments;
|
||||
}
|
||||
|
||||
static FilePath createOutputFilePath(const FilePath &dirPath, const FilePath &fileToAnalyze)
|
||||
{
|
||||
const QString fileName = fileToAnalyze.fileName();
|
||||
@@ -111,7 +69,8 @@ static FilePath createOutputFilePath(const FilePath &dirPath, const FilePath &fi
|
||||
return {};
|
||||
}
|
||||
|
||||
GroupItem clangToolTask(const AnalyzeUnits &units,
|
||||
GroupItem clangToolTask(CppEditor::ClangToolType toolType,
|
||||
const AnalyzeUnits &units,
|
||||
const AnalyzeInputData &input,
|
||||
const AnalyzeSetupHandler &setupHandler,
|
||||
const AnalyzeOutputHandler &outputHandler)
|
||||
@@ -124,8 +83,9 @@ GroupItem clangToolTask(const AnalyzeUnits &units,
|
||||
const Storage<ClangToolStorage> storage;
|
||||
const LoopList iterator(units);
|
||||
|
||||
const auto mainToolArguments = [input, iterator](const ClangToolStorage &data) {
|
||||
const auto mainToolArguments = [input, iterator, toolType](const ClangToolStorage &data) {
|
||||
QStringList result;
|
||||
result << "-p" << ClangToolsCompilationDb::getDb(toolType).parentDir().nativePath();
|
||||
result << "-export-fixes=" + data.outputFilePath.nativePath();
|
||||
if (!input.overlayFilePath.isEmpty() && isVFSOverlaySupported(data.executable))
|
||||
result << "--vfsoverlay=" + input.overlayFilePath;
|
||||
@@ -146,8 +106,6 @@ GroupItem clangToolTask(const AnalyzeUnits &units,
|
||||
return SetupResult::StopWithError;
|
||||
}
|
||||
|
||||
QTC_CHECK(!unit.arguments.contains(QLatin1String("-o")));
|
||||
QTC_CHECK(!unit.arguments.contains(unit.file.nativePath()));
|
||||
QTC_ASSERT(unit.file.exists(), return SetupResult::StopWithError);
|
||||
data->outputFilePath = createOutputFilePath(input.outputDirPath, unit.file);
|
||||
QTC_ASSERT(!data->outputFilePath.isEmpty(), return SetupResult::StopWithError);
|
||||
@@ -163,8 +121,7 @@ GroupItem clangToolTask(const AnalyzeUnits &units,
|
||||
|
||||
const ClangToolStorage &data = *storage;
|
||||
const CommandLine commandLine{data.executable, {checksArguments(unit, input),
|
||||
mainToolArguments(data), "--",
|
||||
clangArguments(unit, input)}};
|
||||
mainToolArguments(data)}};
|
||||
qCDebug(LOG).noquote() << "Starting" << commandLine.toUserOutput();
|
||||
process.setCommand(commandLine);
|
||||
};
|
||||
|
@@ -19,12 +19,14 @@ namespace Internal {
|
||||
|
||||
struct AnalyzeUnit
|
||||
{
|
||||
AnalyzeUnit(const FileInfo &fileInfo,
|
||||
const Utils::FilePath &clangResourceDir,
|
||||
const QString &clangVersion);
|
||||
AnalyzeUnit(const FileInfo &fileInfo, CppEditor::ClangToolType toolType)
|
||||
: file(fileInfo.file)
|
||||
, toolType(toolType)
|
||||
{}
|
||||
|
||||
Utils::FilePath file;
|
||||
QStringList arguments; // without file itself and "-o somePath"
|
||||
CppEditor::ClangToolType toolType;
|
||||
|
||||
};
|
||||
using AnalyzeUnits = QList<AnalyzeUnit>;
|
||||
|
||||
@@ -53,7 +55,8 @@ struct AnalyzeOutputData
|
||||
using AnalyzeSetupHandler = std::function<bool(const AnalyzeUnit &)>;
|
||||
using AnalyzeOutputHandler = std::function<void(const AnalyzeOutputData &)>;
|
||||
|
||||
Tasking::GroupItem clangToolTask(const AnalyzeUnits &units,
|
||||
Tasking::GroupItem clangToolTask(CppEditor::ClangToolType toolType,
|
||||
const AnalyzeUnits &units,
|
||||
const AnalyzeInputData &input,
|
||||
const AnalyzeSetupHandler &setupHandler,
|
||||
const AnalyzeOutputHandler &outputHandler);
|
||||
|
@@ -33,6 +33,8 @@ QtcPlugin {
|
||||
"clangtoolrunner.cpp",
|
||||
"clangtoolrunner.h",
|
||||
"clangtools_global.h",
|
||||
"clangtoolscompilationdb.cpp",
|
||||
"clangtoolscompilationdb.h",
|
||||
"clangtoolstr.h",
|
||||
"clangtoolsconstants.h",
|
||||
"clangtoolsdiagnostic.cpp",
|
||||
|
162
src/plugins/clangtools/clangtoolscompilationdb.cpp
Normal file
162
src/plugins/clangtools/clangtoolscompilationdb.cpp
Normal file
@@ -0,0 +1,162 @@
|
||||
// Copyright (C) 2024 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "clangtoolscompilationdb.h"
|
||||
|
||||
#include "clangtoolsprojectsettings.h"
|
||||
#include "clangtoolstr.h"
|
||||
#include "clangtoolsutils.h"
|
||||
#include "executableinfo.h"
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
#include <coreplugin/messagemanager.h>
|
||||
#include <cppeditor/clangdiagnosticconfigsmodel.h>
|
||||
#include <cppeditor/compilationdb.h>
|
||||
#include <cppeditor/cppmodelmanager.h>
|
||||
#include <utils/async.h>
|
||||
#include <utils/futuresynchronizer.h>
|
||||
#include <utils/temporarydirectory.h>
|
||||
|
||||
#include <QFutureWatcher>
|
||||
|
||||
#include <memory>
|
||||
|
||||
using namespace CppEditor;
|
||||
using namespace Utils;
|
||||
|
||||
namespace ClangTools::Internal {
|
||||
|
||||
class ClangToolsCompilationDb::Private
|
||||
{
|
||||
public:
|
||||
Private(ClangToolType toolType, ClangToolsCompilationDb *q) : q(q), toolType(toolType) {}
|
||||
|
||||
void generate();
|
||||
QString toolName() const { return clangToolName(toolType); }
|
||||
|
||||
static inline std::unique_ptr<ClangToolsCompilationDb> clangTidyDb;
|
||||
static inline std::unique_ptr<ClangToolsCompilationDb> clazyDb;
|
||||
|
||||
ClangToolsCompilationDb * const q;
|
||||
const ClangToolType toolType;
|
||||
TemporaryDirectory dir{toolName()};
|
||||
QFutureWatcher<GenerateCompilationDbResult> generatorWatcher;
|
||||
FutureSynchronizer generatorSynchronizer;
|
||||
bool readyAndUpToDate = false;
|
||||
};
|
||||
|
||||
ClangToolsCompilationDb::ClangToolsCompilationDb(ClangToolType toolType)
|
||||
: d(new Private(toolType, this))
|
||||
{
|
||||
connect(&d->generatorWatcher, &QFutureWatcher<GenerateCompilationDbResult>::finished,
|
||||
this, [this] {
|
||||
const auto result = d->generatorWatcher.result();
|
||||
const bool success = result.has_value();
|
||||
QTC_CHECK(!d->readyAndUpToDate);
|
||||
d->readyAndUpToDate = success;
|
||||
if (success) {
|
||||
Core::MessageManager::writeSilently(
|
||||
Tr::tr("Compilation database for %1 successfully generated at %2.")
|
||||
.arg(d->toolName(), d->dir.path().toUserOutput()));
|
||||
} else {
|
||||
Core::MessageManager::writeDisrupting(
|
||||
Tr::tr("Generating compilation database for %1 failed: %2")
|
||||
.arg(d->toolName(), result.error()));
|
||||
}
|
||||
emit generated(success);
|
||||
});
|
||||
|
||||
connect(ClangToolsSettings::instance(), &BaseAspect::changed,
|
||||
this, &ClangToolsCompilationDb::invalidate);
|
||||
connect(CppModelManager::instance(), &CppModelManager::projectPartsUpdated,
|
||||
this, &ClangToolsCompilationDb::invalidate);
|
||||
}
|
||||
|
||||
ClangToolsCompilationDb::~ClangToolsCompilationDb() { delete d; }
|
||||
|
||||
void ClangToolsCompilationDb::invalidate() { d->readyAndUpToDate = false; }
|
||||
|
||||
FilePath ClangToolsCompilationDb::parentDir() const { return d->dir.path(); }
|
||||
|
||||
bool ClangToolsCompilationDb::generateIfNecessary()
|
||||
{
|
||||
if (d->readyAndUpToDate)
|
||||
return false;
|
||||
d->generate();
|
||||
return true;
|
||||
}
|
||||
|
||||
ClangToolsCompilationDb &ClangToolsCompilationDb::getDb(ClangToolType toolType)
|
||||
{
|
||||
if (toolType == ClangToolType::Tidy) {
|
||||
if (!Private::clangTidyDb)
|
||||
Private::clangTidyDb.reset(new ClangToolsCompilationDb(toolType));
|
||||
return *Private::clangTidyDb;
|
||||
}
|
||||
if (!Private::clazyDb)
|
||||
Private::clazyDb.reset(new ClangToolsCompilationDb(toolType));
|
||||
return *Private::clazyDb;
|
||||
}
|
||||
|
||||
void ClangToolsCompilationDb::Private::generate()
|
||||
{
|
||||
QTC_CHECK(!readyAndUpToDate);
|
||||
|
||||
if (generatorWatcher.isRunning())
|
||||
generatorWatcher.cancel();
|
||||
|
||||
Core::MessageManager::writeSilently(
|
||||
Tr::tr("Generating compilation database for %1 at %2 ...")
|
||||
.arg(clangToolName(toolType), dir.path().toUserOutput()));
|
||||
|
||||
const auto getCompilerOptionsBuilder = [this](const ProjectPart &pp) {
|
||||
const auto projectSettings = ClangToolsProjectSettings::getSettings(pp.project());
|
||||
QTC_ASSERT(projectSettings, return CompilerOptionsBuilder(pp));
|
||||
connect(projectSettings.get(), &ClangToolsProjectSettings::changed,
|
||||
q, &ClangToolsCompilationDb::invalidate);
|
||||
const Id configId = projectSettings->runSettings().diagnosticConfigId();
|
||||
const ClangDiagnosticConfig config = Utils::findOrDefault(
|
||||
ClangToolsSettings::instance()->diagnosticConfigs(),
|
||||
[configId](const ClangDiagnosticConfig &c) { return c.id() == configId; });
|
||||
const auto useBuildSystemWarnings = config.useBuildSystemWarnings()
|
||||
? UseBuildSystemWarnings::Yes
|
||||
: UseBuildSystemWarnings::No;
|
||||
|
||||
const FilePath executable = toolExecutable(toolType);
|
||||
const auto [includeDir, clangVersion] = getClangIncludeDirAndVersion(executable);
|
||||
const FilePath actualClangIncludeDir = Core::ICore::clangIncludeDirectory(
|
||||
clangVersion, includeDir);
|
||||
|
||||
CompilerOptionsBuilder optionsBuilder(pp,
|
||||
UseSystemHeader::No,
|
||||
UseTweakedHeaderPaths::Tools,
|
||||
UseLanguageDefines::No,
|
||||
useBuildSystemWarnings,
|
||||
actualClangIncludeDir);
|
||||
optionsBuilder.build(ProjectFile::Unclassified, UsePrecompiledHeaders::No);
|
||||
if (useBuildSystemWarnings == UseBuildSystemWarnings::No) {
|
||||
for (const QString &opt : config.clangOptions())
|
||||
optionsBuilder.add(opt, true);
|
||||
}
|
||||
const QStringList extraArgsToPrepend = extraClangToolsPrependOptions();
|
||||
for (const QString &arg : extraArgsToPrepend)
|
||||
optionsBuilder.prepend(arg);
|
||||
const QStringList extraArgsToAppend = extraClangToolsAppendOptions();
|
||||
for (const QString &arg : extraArgsToAppend)
|
||||
optionsBuilder.add(arg);
|
||||
|
||||
return optionsBuilder;
|
||||
};
|
||||
|
||||
generatorWatcher.setFuture(
|
||||
Utils::asyncRun(
|
||||
&generateCompilationDB,
|
||||
CppModelManager::projectInfos(),
|
||||
dir.path(),
|
||||
CompilationDbPurpose::Analysis,
|
||||
ClangDiagnosticConfigsModel::globalDiagnosticOptions(),
|
||||
getCompilerOptionsBuilder));
|
||||
generatorSynchronizer.addFuture(generatorWatcher.future());
|
||||
}
|
||||
|
||||
} // namespace ClangTools::Internal
|
33
src/plugins/clangtools/clangtoolscompilationdb.h
Normal file
33
src/plugins/clangtools/clangtoolscompilationdb.h
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright (C) 2024 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
#pragma once
|
||||
|
||||
#include <cppeditor/clangdiagnosticconfig.h>
|
||||
#include <utils/filepath.h>
|
||||
|
||||
#include <QObject>
|
||||
|
||||
namespace ClangTools::Internal {
|
||||
class ClangToolsCompilationDb : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
~ClangToolsCompilationDb();
|
||||
|
||||
bool generateIfNecessary();
|
||||
Utils::FilePath parentDir() const;
|
||||
|
||||
static ClangToolsCompilationDb &getDb(CppEditor::ClangToolType toolType);
|
||||
|
||||
signals:
|
||||
void generated(bool success);
|
||||
|
||||
private:
|
||||
explicit ClangToolsCompilationDb(CppEditor::ClangToolType toolType);
|
||||
void invalidate();
|
||||
|
||||
class Private;
|
||||
Private * const d;
|
||||
};
|
||||
|
||||
} // namespace ClangTools::Internal
|
@@ -205,7 +205,7 @@ void DocumentClangToolRunner::run()
|
||||
const auto [includeDir, clangVersion] = getClangIncludeDirAndVersion(executable);
|
||||
if (includeDir.isEmpty() || clangVersion.isEmpty())
|
||||
return;
|
||||
const AnalyzeUnits units{{m_fileInfo, includeDir, clangVersion}};
|
||||
const AnalyzeUnits units{{m_fileInfo, tool}};
|
||||
const auto diagnosticFilter = [mappedPath = vfso().autoSavedFilePath(m_document)](
|
||||
const FilePath &path) { return path == mappedPath; };
|
||||
const AnalyzeInputData input{tool,
|
||||
@@ -219,7 +219,8 @@ void DocumentClangToolRunner::run()
|
||||
return !m_document->isModified() || isVFSOverlaySupported(executable);
|
||||
};
|
||||
const auto outputHandler = [this](const AnalyzeOutputData &output) { onDone(output); };
|
||||
tasks.append(Group{finishAllAndSuccess, clangToolTask(units, input, setupHandler, outputHandler)});
|
||||
tasks.append(Group{finishAllAndSuccess,
|
||||
clangToolTask(tool, units, input, setupHandler, outputHandler)});
|
||||
};
|
||||
addClangTool(ClangToolType::Tidy);
|
||||
addClangTool(ClangToolType::Clazy);
|
||||
|
@@ -135,6 +135,8 @@ static QJsonObject createFileObject(const FilePath &buildDir,
|
||||
}
|
||||
} else {
|
||||
args = clangOptionsForFile(projFile, projectPart, projectPartOptions, usePch, clStyle);
|
||||
if (purpose == CompilationDbPurpose::Analysis && projFile.isHeader())
|
||||
args << "-Wno-pragma-once-outside-header";
|
||||
args.prepend("clang"); // TODO: clang-cl for MSVC targets? Does it matter at all what we put here?
|
||||
}
|
||||
|
||||
|
@@ -19,7 +19,7 @@ class ClangDiagnosticConfig;
|
||||
|
||||
using GenerateCompilationDbResult = Utils::expected_str<Utils::FilePath>;
|
||||
using GetOptionsBuilder = std::function<CompilerOptionsBuilder(const ProjectPart &)>;
|
||||
enum class CompilationDbPurpose { Project, CodeModel };
|
||||
enum class CompilationDbPurpose { Project, CodeModel, Analysis };
|
||||
|
||||
QJsonArray CPPEDITOR_EXPORT fullProjectPartOptions(
|
||||
const CppEditor::CompilerOptionsBuilder &optionsBuilder,
|
||||
|
Reference in New Issue
Block a user