forked from qt-creator/qt-creator
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:
@@ -53,28 +53,6 @@ static bool isClMode(const QStringList &options)
|
||||
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)
|
||||
{
|
||||
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 ClangTools
|
||||
|
@@ -48,13 +48,5 @@ public:
|
||||
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 ClangTools
|
||||
|
@@ -717,7 +717,6 @@ void ClangTool::startTool(ClangTool::FileSelection fileSelection,
|
||||
|
||||
Diagnostics ClangTool::read(OutputFileFormat outputFileFormat,
|
||||
const QString &logFilePath,
|
||||
const QString &mainFilePath,
|
||||
const QSet<FilePath> &projectFiles,
|
||||
QString *errorMessage) const
|
||||
{
|
||||
@@ -730,10 +729,8 @@ Diagnostics ClangTool::read(OutputFileFormat outputFileFormat,
|
||||
acceptFromFilePath,
|
||||
errorMessage);
|
||||
}
|
||||
return readSerializedDiagnostics(Utils::FilePath::fromString(logFilePath),
|
||||
Utils::FilePath::fromString(mainFilePath),
|
||||
acceptFromFilePath,
|
||||
errorMessage);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
FileInfos ClangTool::collectFileInfos(Project *project, FileSelection fileSelection)
|
||||
|
@@ -96,7 +96,6 @@ public:
|
||||
|
||||
Diagnostics read(OutputFileFormat outputFileFormat,
|
||||
const QString &logFilePath,
|
||||
const QString &mainFilePath,
|
||||
const QSet<Utils::FilePath> &projectFiles,
|
||||
QString *errorMessage) const;
|
||||
|
||||
|
@@ -236,12 +236,8 @@ QList<RunnerCreator> ClangToolRunWorker::runnerCreators()
|
||||
if (m_diagnosticConfig.isClangTidyEnabled())
|
||||
creators << [this]() { return createRunner<ClangTidyRunner>(); };
|
||||
|
||||
if (m_diagnosticConfig.isClazyEnabled()) {
|
||||
if (!qEnvironmentVariable("QTC_USE_CLAZY_STANDALONE_PATH").isEmpty())
|
||||
if (m_diagnosticConfig.isClazyEnabled())
|
||||
creators << [this]() { return createRunner<ClazyStandaloneRunner>(); };
|
||||
else
|
||||
creators << [this]() { return createRunner<ClazyPluginRunner>(); };
|
||||
}
|
||||
|
||||
return creators;
|
||||
}
|
||||
@@ -388,7 +384,6 @@ void ClangToolRunWorker::onRunnerFinishedWithSuccess(const QString &filePath)
|
||||
QString errorMessage;
|
||||
const Diagnostics diagnostics = tool()->read(runner->outputFileFormat(),
|
||||
outputFilePath,
|
||||
filePath,
|
||||
m_projectFiles,
|
||||
&errorMessage);
|
||||
|
||||
|
@@ -4,9 +4,6 @@ include(../../shared/clang/clang_defines.pri)
|
||||
|
||||
requires(!isEmpty(LLVM_VERSION))
|
||||
|
||||
LIBS += $$LIBCLANG_LIBS
|
||||
INCLUDEPATH += $$LLVM_INCLUDEPATH
|
||||
|
||||
include(../../shared/yaml-cpp/yaml-cpp_installation.pri)
|
||||
isEmpty(EXTERNAL_YAML_CPP_FOUND) {
|
||||
DEFINES += YAML_CPP_DLL
|
||||
|
@@ -12,7 +12,6 @@ QtcPlugin {
|
||||
Depends { name: "QtcSsh" }
|
||||
Depends { name: "Utils" }
|
||||
|
||||
Depends { name: "libclang"; required: false }
|
||||
Depends { name: "yaml-cpp" }
|
||||
Depends { name: "clang_defines" }
|
||||
|
||||
@@ -27,13 +26,6 @@ QtcPlugin {
|
||||
"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: [
|
||||
"clangfileinfo.h",
|
||||
"clangfixitsrefactoringchanges.cpp",
|
||||
|
@@ -27,198 +27,17 @@
|
||||
|
||||
#include <cpptools/cppprojectfile.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QObject>
|
||||
#include <QRegularExpression>
|
||||
#include <QXmlStreamReader>
|
||||
|
||||
#include <utils/executeondestruction.h>
|
||||
#include <utils/fileutils.h>
|
||||
#include <utils/hostosinfo.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/textutils.h>
|
||||
|
||||
#include <clang-c/Index.h>
|
||||
|
||||
#include <yaml-cpp/yaml.h>
|
||||
|
||||
namespace ClangTools {
|
||||
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)
|
||||
{
|
||||
QFileInfo fi(filePath.toFileInfo());
|
||||
@@ -234,17 +53,6 @@ static bool checkFilePath(const Utils::FilePath &filePath, QString *errorMessage
|
||||
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,
|
||||
int offset,
|
||||
int startLine)
|
||||
|
@@ -34,16 +34,10 @@ namespace Utils { class FilePath; }
|
||||
namespace ClangTools {
|
||||
namespace Internal {
|
||||
|
||||
enum class OutputFileFormat { Serialized, Yaml };
|
||||
enum class OutputFileFormat { Yaml };
|
||||
|
||||
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"
|
||||
Diagnostics readExportedDiagnostics(const Utils::FilePath &logFilePath,
|
||||
const AcceptDiagsFromFilePath &acceptFromFilePath,
|
||||
|
@@ -153,7 +153,6 @@ QString clazyStandaloneFallbackExecutable()
|
||||
{
|
||||
return findValidExecutable({
|
||||
shippedClazyStandaloneExecutable(),
|
||||
qEnvironmentVariable("QTC_USE_CLAZY_STANDALONE_PATH"),
|
||||
Constants::CLAZY_STANDALONE_EXECUTABLE_NAME,
|
||||
});
|
||||
}
|
||||
|
@@ -95,10 +95,6 @@ SettingsWidget::SettingsWidget()
|
||||
path,
|
||||
"ClangTools.ClangTidyExecutable.History");
|
||||
|
||||
if (qEnvironmentVariable("QTC_USE_CLAZY_STANDALONE_PATH").isEmpty()) {
|
||||
m_ui->clazyStandalonePathChooser->setVisible(false);
|
||||
m_ui->clazyStandaloneLabel->setVisible(false);
|
||||
} else {
|
||||
placeHolderText = shippedClazyStandaloneExecutable();
|
||||
path = m_settings->clazyStandaloneExecutable();
|
||||
if (path.isEmpty() && placeHolderText.isEmpty())
|
||||
@@ -108,7 +104,6 @@ SettingsWidget::SettingsWidget()
|
||||
placeHolderText,
|
||||
path,
|
||||
"ClangTools.ClazyStandaloneExecutable.History");
|
||||
}
|
||||
|
||||
//
|
||||
// Group box "Run Options"
|
||||
|
Reference in New Issue
Block a user