ClangTools: Remove dependency to libclang and custom clang binary

Before this change, we've invoked a custom clang binary that had clazy
statically compiled into it. The invocation also ensured that the
diagnostics were serialized to a file, so that libclang could be used
afterwards to read them.

As the clazy-standalone executable supports exporting diagnostics to a
YAML file now (just as clang-tidy) and Qt Creator ships it already, rely
on that executable alone instead of the clang/libclang combo.

While we do not depend on any clang header or library at build-time now,
the CompilerOptionsBuilder constructor still needs the CLANG_VERSION and
CLANG_RESOURCE_DIR pieces from llvm-config. This dependency should be
removed as next.

Change-Id: I4fa5753ab09008fd24bc5247b28c4836b5e8ca45
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Nikolai Kosjar
2020-05-13 14:47:35 +02:00
parent e2a68edbc1
commit 9fc2fda07e
11 changed files with 14 additions and 282 deletions

View File

@@ -53,28 +53,6 @@ static bool isClMode(const QStringList &options)
return options.contains("--driver-mode=cl"); return options.contains("--driver-mode=cl");
} }
static QStringList serializeDiagnosticsArguments(const QStringList &baseOptions,
const QString &outputFilePath)
{
const QStringList serializeArgs{"-serialize-diagnostics", outputFilePath};
if (isClMode(baseOptions))
return clangArgsForCl(serializeArgs);
return serializeArgs;
}
static QStringList clazyPluginArguments(const ClangDiagnosticConfig diagnosticConfig)
{
QStringList arguments;
if (diagnosticConfig.isClazyEnabled()) {
arguments << XclangArgs({"-add-plugin", "clazy"});
if (!diagnosticConfig.clazyChecks().isEmpty())
arguments << XclangArgs({"-plugin-arg-clazy", diagnosticConfig.clazyChecks()});
}
return arguments;
}
static QStringList tidyChecksArguments(const ClangDiagnosticConfig diagnosticConfig) static QStringList tidyChecksArguments(const ClangDiagnosticConfig diagnosticConfig)
{ {
const ClangDiagnosticConfig::TidyMode tidyMode = diagnosticConfig.clangTidyMode(); const ClangDiagnosticConfig::TidyMode tidyMode = diagnosticConfig.clangTidyMode();
@@ -147,19 +125,5 @@ ClazyStandaloneRunner::ClazyStandaloneRunner(const ClangDiagnosticConfig &config
}); });
} }
ClazyPluginRunner::ClazyPluginRunner(const ClangDiagnosticConfig &config, QObject *parent)
: ClangToolRunner(parent)
{
setName(tr("Clazy"));
setOutputFileFormat(OutputFileFormat::Serialized);
setExecutable(Core::ICore::clangExecutable(CLANG_BINDIR));
setArgsCreator([this, config](const QStringList &baseOptions) {
return serializeDiagnosticsArguments(baseOptions, outputFilePath())
<< clazyPluginArguments(config)
<< clangArguments(config, baseOptions)
<< QDir::toNativeSeparators(fileToAnalyze());
});
}
} // namespace Internal } // namespace Internal
} // namespace ClangTools } // namespace ClangTools

View File

@@ -48,13 +48,5 @@ public:
ClazyStandaloneRunner(const CppTools::ClangDiagnosticConfig &config, QObject *parent = nullptr); ClazyStandaloneRunner(const CppTools::ClangDiagnosticConfig &config, QObject *parent = nullptr);
}; };
class ClazyPluginRunner final : public ClangToolRunner
{
Q_OBJECT
public:
ClazyPluginRunner(const CppTools::ClangDiagnosticConfig &config, QObject *parent = nullptr);
};
} // namespace Internal } // namespace Internal
} // namespace ClangTools } // namespace ClangTools

View File

@@ -717,7 +717,6 @@ void ClangTool::startTool(ClangTool::FileSelection fileSelection,
Diagnostics ClangTool::read(OutputFileFormat outputFileFormat, Diagnostics ClangTool::read(OutputFileFormat outputFileFormat,
const QString &logFilePath, const QString &logFilePath,
const QString &mainFilePath,
const QSet<FilePath> &projectFiles, const QSet<FilePath> &projectFiles,
QString *errorMessage) const QString *errorMessage) const
{ {
@@ -730,10 +729,8 @@ Diagnostics ClangTool::read(OutputFileFormat outputFileFormat,
acceptFromFilePath, acceptFromFilePath,
errorMessage); errorMessage);
} }
return readSerializedDiagnostics(Utils::FilePath::fromString(logFilePath),
Utils::FilePath::fromString(mainFilePath), return {};
acceptFromFilePath,
errorMessage);
} }
FileInfos ClangTool::collectFileInfos(Project *project, FileSelection fileSelection) FileInfos ClangTool::collectFileInfos(Project *project, FileSelection fileSelection)

View File

@@ -96,7 +96,6 @@ public:
Diagnostics read(OutputFileFormat outputFileFormat, Diagnostics read(OutputFileFormat outputFileFormat,
const QString &logFilePath, const QString &logFilePath,
const QString &mainFilePath,
const QSet<Utils::FilePath> &projectFiles, const QSet<Utils::FilePath> &projectFiles,
QString *errorMessage) const; QString *errorMessage) const;

View File

@@ -236,12 +236,8 @@ QList<RunnerCreator> ClangToolRunWorker::runnerCreators()
if (m_diagnosticConfig.isClangTidyEnabled()) if (m_diagnosticConfig.isClangTidyEnabled())
creators << [this]() { return createRunner<ClangTidyRunner>(); }; creators << [this]() { return createRunner<ClangTidyRunner>(); };
if (m_diagnosticConfig.isClazyEnabled()) { if (m_diagnosticConfig.isClazyEnabled())
if (!qEnvironmentVariable("QTC_USE_CLAZY_STANDALONE_PATH").isEmpty())
creators << [this]() { return createRunner<ClazyStandaloneRunner>(); }; creators << [this]() { return createRunner<ClazyStandaloneRunner>(); };
else
creators << [this]() { return createRunner<ClazyPluginRunner>(); };
}
return creators; return creators;
} }
@@ -388,7 +384,6 @@ void ClangToolRunWorker::onRunnerFinishedWithSuccess(const QString &filePath)
QString errorMessage; QString errorMessage;
const Diagnostics diagnostics = tool()->read(runner->outputFileFormat(), const Diagnostics diagnostics = tool()->read(runner->outputFileFormat(),
outputFilePath, outputFilePath,
filePath,
m_projectFiles, m_projectFiles,
&errorMessage); &errorMessage);

View File

@@ -4,9 +4,6 @@ include(../../shared/clang/clang_defines.pri)
requires(!isEmpty(LLVM_VERSION)) requires(!isEmpty(LLVM_VERSION))
LIBS += $$LIBCLANG_LIBS
INCLUDEPATH += $$LLVM_INCLUDEPATH
include(../../shared/yaml-cpp/yaml-cpp_installation.pri) include(../../shared/yaml-cpp/yaml-cpp_installation.pri)
isEmpty(EXTERNAL_YAML_CPP_FOUND) { isEmpty(EXTERNAL_YAML_CPP_FOUND) {
DEFINES += YAML_CPP_DLL DEFINES += YAML_CPP_DLL

View File

@@ -12,7 +12,6 @@ QtcPlugin {
Depends { name: "QtcSsh" } Depends { name: "QtcSsh" }
Depends { name: "Utils" } Depends { name: "Utils" }
Depends { name: "libclang"; required: false }
Depends { name: "yaml-cpp" } Depends { name: "yaml-cpp" }
Depends { name: "clang_defines" } Depends { name: "clang_defines" }
@@ -27,13 +26,6 @@ QtcPlugin {
"QmakeProjectManager", "QmakeProjectManager",
] ]
condition: libclang.present
cpp.includePaths: base.concat(libclang.llvmIncludeDir)
cpp.libraryPaths: base.concat(libclang.llvmLibDir)
cpp.dynamicLibraries: base.concat(libclang.llvmLibs)
cpp.rpaths: base.concat(libclang.llvmLibDir)
files: [ files: [
"clangfileinfo.h", "clangfileinfo.h",
"clangfixitsrefactoringchanges.cpp", "clangfixitsrefactoringchanges.cpp",

View File

@@ -27,198 +27,17 @@
#include <cpptools/cppprojectfile.h> #include <cpptools/cppprojectfile.h>
#include <QDebug>
#include <QDir> #include <QDir>
#include <QFile>
#include <QFileInfo> #include <QFileInfo>
#include <QObject>
#include <QRegularExpression>
#include <QXmlStreamReader>
#include <utils/executeondestruction.h>
#include <utils/fileutils.h> #include <utils/fileutils.h>
#include <utils/hostosinfo.h>
#include <utils/qtcassert.h>
#include <utils/textutils.h> #include <utils/textutils.h>
#include <clang-c/Index.h>
#include <yaml-cpp/yaml.h> #include <yaml-cpp/yaml.h>
namespace ClangTools { namespace ClangTools {
namespace Internal { namespace Internal {
static QString fromCXString(CXString &&cxString)
{
QString result = QString::fromUtf8(clang_getCString(cxString));
clang_disposeString(cxString);
return result;
}
static Debugger::DiagnosticLocation diagLocationFromSourceLocation(CXSourceLocation cxLocation)
{
CXFile file;
unsigned line;
unsigned column;
clang_getSpellingLocation(cxLocation, &file, &line, &column, nullptr);
Debugger::DiagnosticLocation location;
location.filePath = fromCXString(clang_getFileName(file));
location.filePath = QDir::cleanPath(location.filePath); // Normalize to find duplicates later
location.line = line;
location.column = column;
return location;
}
static QString cxDiagnosticType(const CXDiagnostic cxDiagnostic)
{
const CXDiagnosticSeverity severity = clang_getDiagnosticSeverity(cxDiagnostic);
switch (severity) {
case CXDiagnostic_Note:
return QString("note");
case CXDiagnostic_Warning:
return QString("warning");
case CXDiagnostic_Error:
return QString("error");
case CXDiagnostic_Fatal:
return QString("fatal");
case CXDiagnostic_Ignored:
return QString("ignored");
}
return QString("ignored");
}
static ExplainingStep buildChildDiagnostic(const CXDiagnostic cxDiagnostic)
{
ExplainingStep diagnosticStep;
QString type = cxDiagnosticType(cxDiagnostic);
if (type == QStringLiteral("ignored"))
return diagnosticStep;
const CXSourceLocation cxLocation = clang_getDiagnosticLocation(cxDiagnostic);
diagnosticStep.location = diagLocationFromSourceLocation(cxLocation);
diagnosticStep.message = fromCXString(clang_getDiagnosticSpelling(cxDiagnostic));
return diagnosticStep;
}
static bool isInvalidDiagnosticLocation(const Diagnostic &diagnostic, const ExplainingStep &child,
const QString &nativeFilePath)
{
// When main file is considered included by itself - this diagnostic has invalid location.
// This case usually happens when original diagnostic comes from system header but
// has main file name set in the source location instead (which is incorrect).
return child.message.indexOf(nativeFilePath) >= 0
&& child.message.indexOf("in file included from") >= 0
&& diagnostic.location.filePath == nativeFilePath;
}
static ExplainingStep buildFixIt(const CXDiagnostic cxDiagnostic, unsigned index)
{
ExplainingStep fixItStep;
CXSourceRange cxFixItRange;
fixItStep.isFixIt = true;
fixItStep.message = fromCXString(clang_getDiagnosticFixIt(cxDiagnostic, index, &cxFixItRange));
fixItStep.location = diagLocationFromSourceLocation(clang_getRangeStart(cxFixItRange));
fixItStep.ranges.push_back(fixItStep.location);
fixItStep.ranges.push_back(diagLocationFromSourceLocation(clang_getRangeEnd(cxFixItRange)));
return fixItStep;
}
static Diagnostic buildDiagnostic(const CXDiagnostic cxDiagnostic,
const AcceptDiagsFromFilePath &acceptFromFilePath,
const QString &nativeFilePath)
{
Diagnostic diagnostic;
diagnostic.type = cxDiagnosticType(cxDiagnostic);
if (diagnostic.type == QStringLiteral("ignored"))
return diagnostic;
const CXSourceLocation cxLocation = clang_getDiagnosticLocation(cxDiagnostic);
if (clang_Location_isInSystemHeader(cxLocation))
return diagnostic;
diagnostic.location = diagLocationFromSourceLocation(cxLocation);
const auto diagnosticFilePath = Utils::FilePath::fromString(diagnostic.location.filePath);
if (acceptFromFilePath && !acceptFromFilePath(diagnosticFilePath))
return diagnostic;
// TODO: Introduce CppTools::ProjectFile::isGenerated to filter these out properly
const QString fileName = diagnosticFilePath.fileName();
if ((fileName.startsWith("ui_") && fileName.endsWith(".h")) || fileName.endsWith(".moc"))
return diagnostic;
CXDiagnosticSet cxChildDiagnostics = clang_getChildDiagnostics(cxDiagnostic);
Utils::ExecuteOnDestruction onBuildExit([&]() {
clang_disposeDiagnosticSet(cxChildDiagnostics);
});
using CppTools::ProjectFile;
const bool isHeaderFile = ProjectFile::isHeader(
ProjectFile::classify(diagnostic.location.filePath));
for (unsigned i = 0; i < clang_getNumDiagnosticsInSet(cxChildDiagnostics); ++i) {
CXDiagnostic cxDiagnostic = clang_getDiagnosticInSet(cxChildDiagnostics, i);
Utils::ExecuteOnDestruction cleanUpDiagnostic([&]() {
clang_disposeDiagnostic(cxDiagnostic);
});
const ExplainingStep diagnosticStep = buildChildDiagnostic(cxDiagnostic);
if (diagnosticStep.isValid())
continue;
if (isHeaderFile && diagnosticStep.message.contains("in file included from"))
continue;
if (isInvalidDiagnosticLocation(diagnostic, diagnosticStep, nativeFilePath))
return diagnostic;
diagnostic.explainingSteps.push_back(diagnosticStep);
}
const unsigned fixItCount = clang_getDiagnosticNumFixIts(cxDiagnostic);
diagnostic.hasFixits = fixItCount != 0;
for (unsigned i = 0; i < fixItCount; ++i)
diagnostic.explainingSteps.push_back(buildFixIt(cxDiagnostic, i));
diagnostic.description = fromCXString(clang_getDiagnosticSpelling(cxDiagnostic));
diagnostic.category = fromCXString(clang_getDiagnosticCategoryText(cxDiagnostic));
return diagnostic;
}
static Diagnostics readSerializedDiagnostics_helper(const Utils::FilePath &logFilePath,
const Utils::FilePath &mainFilePath,
const AcceptDiagsFromFilePath &acceptFromFilePath)
{
Diagnostics list;
CXLoadDiag_Error error;
CXString errorString;
CXDiagnosticSet diagnostics = clang_loadDiagnostics(logFilePath.toString().toStdString().c_str(),
&error,
&errorString);
if (error != CXLoadDiag_None || !diagnostics)
return list;
Utils::ExecuteOnDestruction onReadExit([&]() {
clang_disposeDiagnosticSet(diagnostics);
});
const QString nativeFilePath = QDir::toNativeSeparators(mainFilePath.toString());
for (unsigned i = 0; i < clang_getNumDiagnosticsInSet(diagnostics); ++i) {
CXDiagnostic cxDiagnostic = clang_getDiagnosticInSet(diagnostics, i);
Utils::ExecuteOnDestruction cleanUpDiagnostic([&]() {
clang_disposeDiagnostic(cxDiagnostic);
});
const Diagnostic diagnostic = buildDiagnostic(cxDiagnostic, acceptFromFilePath, nativeFilePath);
if (!diagnostic.isValid())
continue;
list.push_back(diagnostic);
}
return list;
}
static bool checkFilePath(const Utils::FilePath &filePath, QString *errorMessage) static bool checkFilePath(const Utils::FilePath &filePath, QString *errorMessage)
{ {
QFileInfo fi(filePath.toFileInfo()); QFileInfo fi(filePath.toFileInfo());
@@ -234,17 +53,6 @@ static bool checkFilePath(const Utils::FilePath &filePath, QString *errorMessage
return true; return true;
} }
Diagnostics readSerializedDiagnostics(const Utils::FilePath &logFilePath,
const Utils::FilePath &mainFilePath,
const AcceptDiagsFromFilePath &acceptFromFilePath,
QString *errorMessage)
{
if (!checkFilePath(logFilePath, errorMessage))
return {};
return readSerializedDiagnostics_helper(logFilePath, mainFilePath, acceptFromFilePath);
}
Utils::optional<LineColumnInfo> byteOffsetInUtf8TextToLineColumn(const char *text, Utils::optional<LineColumnInfo> byteOffsetInUtf8TextToLineColumn(const char *text,
int offset, int offset,
int startLine) int startLine)

View File

@@ -34,16 +34,10 @@ namespace Utils { class FilePath; }
namespace ClangTools { namespace ClangTools {
namespace Internal { namespace Internal {
enum class OutputFileFormat { Serialized, Yaml }; enum class OutputFileFormat { Yaml };
using AcceptDiagsFromFilePath = std::function<bool(const Utils::FilePath &)>; using AcceptDiagsFromFilePath = std::function<bool(const Utils::FilePath &)>;
// Reads diagnostics generated by "clang -serialize-diagnostics path/to/file"
Diagnostics readSerializedDiagnostics(const Utils::FilePath &logFilePath,
const Utils::FilePath &mainFilePath,
const AcceptDiagsFromFilePath &acceptFromFilePath,
QString *errorMessage);
// Reads diagnostics generated by "clang-tidy/clazy-standalone -export-fixes=path/to/file" // Reads diagnostics generated by "clang-tidy/clazy-standalone -export-fixes=path/to/file"
Diagnostics readExportedDiagnostics(const Utils::FilePath &logFilePath, Diagnostics readExportedDiagnostics(const Utils::FilePath &logFilePath,
const AcceptDiagsFromFilePath &acceptFromFilePath, const AcceptDiagsFromFilePath &acceptFromFilePath,

View File

@@ -153,7 +153,6 @@ QString clazyStandaloneFallbackExecutable()
{ {
return findValidExecutable({ return findValidExecutable({
shippedClazyStandaloneExecutable(), shippedClazyStandaloneExecutable(),
qEnvironmentVariable("QTC_USE_CLAZY_STANDALONE_PATH"),
Constants::CLAZY_STANDALONE_EXECUTABLE_NAME, Constants::CLAZY_STANDALONE_EXECUTABLE_NAME,
}); });
} }

View File

@@ -95,10 +95,6 @@ SettingsWidget::SettingsWidget()
path, path,
"ClangTools.ClangTidyExecutable.History"); "ClangTools.ClangTidyExecutable.History");
if (qEnvironmentVariable("QTC_USE_CLAZY_STANDALONE_PATH").isEmpty()) {
m_ui->clazyStandalonePathChooser->setVisible(false);
m_ui->clazyStandaloneLabel->setVisible(false);
} else {
placeHolderText = shippedClazyStandaloneExecutable(); placeHolderText = shippedClazyStandaloneExecutable();
path = m_settings->clazyStandaloneExecutable(); path = m_settings->clazyStandaloneExecutable();
if (path.isEmpty() && placeHolderText.isEmpty()) if (path.isEmpty() && placeHolderText.isEmpty())
@@ -108,7 +104,6 @@ SettingsWidget::SettingsWidget()
placeHolderText, placeHolderText,
path, path,
"ClangTools.ClazyStandaloneExecutable.History"); "ClangTools.ClazyStandaloneExecutable.History");
}
// //
// Group box "Run Options" // Group box "Run Options"