diff options
author | Christian Kandeler <christian.kandeler@qt.io> | 2021-02-23 13:51:41 +0100 |
---|---|---|
committer | Christian Kandeler <christian.kandeler@qt.io> | 2021-04-22 07:32:58 +0000 |
commit | ecafdb7543927ffe8a9066ce24ed532a097fa21d (patch) | |
tree | 5f77d9fc9af3967af2bda56c052ccd4a351e0703 /src/plugins/clangcodemodel/clangutils.cpp | |
parent | 8bacd9bdc49c376af69c81ac9acf2e9db1f10cb5 (diff) | |
download | qt-creator-ecafdb7543927ffe8a9066ce24ed532a097fa21d.tar.gz |
ClangCodeModel: Add experimental clangd support
If the user has enabled clangd (default is off), we start up one instance
per project when it is opened/changed (including build config switches),
and trigger background indexing.
So far, the index is used to provide results for locators and "Find
Usages".
Per-document functionality such as semantic highlighting and completion
is still provided by libclang.
Change-Id: I12532fca1b9c6278baab560e7238cba6189cde9f
Reviewed-by: David Schulz <david.schulz@qt.io>
Diffstat (limited to 'src/plugins/clangcodemodel/clangutils.cpp')
-rw-r--r-- | src/plugins/clangcodemodel/clangutils.cpp | 198 |
1 files changed, 171 insertions, 27 deletions
diff --git a/src/plugins/clangcodemodel/clangutils.cpp b/src/plugins/clangcodemodel/clangutils.cpp index f46b455197..95b9364e0c 100644 --- a/src/plugins/clangcodemodel/clangutils.cpp +++ b/src/plugins/clangcodemodel/clangutils.cpp @@ -27,6 +27,7 @@ #include "clangeditordocumentprocessor.h" #include "clangmodelmanagersupport.h" +#include "clangprojectsettings.h" #include <clangsupport/tokeninfocontainer.h> @@ -104,14 +105,6 @@ private: } }; -QStringList createClangOptions(const ProjectPart &projectPart, - UseBuildSystemWarnings useBuildSystemWarnings, - ProjectFile::Kind fileKind) -{ - return LibClangOptionsBuilder(projectPart, useBuildSystemWarnings) - .build(fileKind, UsePrecompiledHeaders::No); -} - ProjectPart::Ptr projectPartForFile(const QString &filePath) { if (const auto parser = CppTools::BaseEditorDocumentParser::get(filePath)) @@ -354,34 +347,45 @@ static QStringList projectPartArguments(const ProjectPart &projectPart) static QJsonObject createFileObject(const FilePath &buildDir, const QStringList &arguments, const ProjectPart &projectPart, - const ProjectFile &projFile) + const ProjectFile &projFile, + CompilationDbPurpose purpose) { QJsonObject fileObject; fileObject["file"] = projFile.path; - QJsonArray args = QJsonArray::fromStringList(arguments); - - const ProjectFile::Kind kind = ProjectFile::classify(projFile.path); - if (projectPart.toolchainType == ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID - || projectPart.toolchainType == ProjectExplorer::Constants::CLANG_CL_TOOLCHAIN_TYPEID) { - if (ProjectFile::isC(kind)) - args.append("/TC"); - else if (ProjectFile::isCxx(kind)) - args.append("/TP"); + QJsonArray args; + + if (purpose == CompilationDbPurpose::Project) { + args = QJsonArray::fromStringList(arguments); + + const ProjectFile::Kind kind = ProjectFile::classify(projFile.path); + if (projectPart.toolchainType == ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID + || projectPart.toolchainType == ProjectExplorer::Constants::CLANG_CL_TOOLCHAIN_TYPEID) { + if (ProjectFile::isC(kind)) + args.append("/TC"); + else if (ProjectFile::isCxx(kind)) + args.append("/TP"); + } else { + QStringList langOption + = createLanguageOptionGcc(kind, + projectPart.languageExtensions + & LanguageExtension::ObjectiveC); + for (const QString &langOptionPart : langOption) + args.append(langOptionPart); + } } else { - QStringList langOption - = createLanguageOptionGcc(kind, - projectPart.languageExtensions - & LanguageExtension::ObjectiveC); - for (const QString &langOptionPart : langOption) - args.append(langOptionPart); + // TODO: Do we really need to re-calculate the project part options per source file? + args = QJsonArray::fromStringList(createClangOptions(projectPart, projFile.path).second); + args.prepend("clang"); // TODO: clang-cl for MSVC targets? Does it matter at all what we put here? } + args.append(QDir::toNativeSeparators(projFile.path)); fileObject["arguments"] = args; fileObject["directory"] = buildDir.toString(); return fileObject; } -GenerateCompilationDbResult generateCompilationDB(CppTools::ProjectInfo projectInfo) +GenerateCompilationDbResult generateCompilationDB(CppTools::ProjectInfo projectInfo, + CompilationDbPurpose purpose) { const FilePath buildDir = buildDirectory(*projectInfo.project()); QTC_ASSERT(!buildDir.isEmpty(), return GenerateCompilationDbResult(QString(), @@ -400,9 +404,12 @@ GenerateCompilationDbResult generateCompilationDB(CppTools::ProjectInfo projectI compileCommandsFile.write("["); for (ProjectPart::Ptr projectPart : projectInfo.projectParts()) { - const QStringList args = projectPartArguments(*projectPart); + QStringList args; + if (purpose == CompilationDbPurpose::Project) + args = projectPartArguments(*projectPart); for (const ProjectFile &projFile : projectPart->files) { - const QJsonObject json = createFileObject(buildDir, args, *projectPart, projFile); + const QJsonObject json = createFileObject(buildDir, args, *projectPart, projFile, + purpose); if (compileCommandsFile.size() > 1) compileCommandsFile.write(","); compileCommandsFile.write('\n' + QJsonDocument(json).toJson().trimmed()); @@ -474,5 +481,142 @@ QString DiagnosticTextInfo::clazyCheckName(const QString &option) return option; } + +namespace { +static ClangProjectSettings &getProjectSettings(ProjectExplorer::Project *project) +{ + QTC_CHECK(project); + return ClangModelManagerSupport::instance()->projectSettings(project); +} + +// TODO: Can we marry this with CompilerOptionsBuilder? +class FileOptionsBuilder +{ +public: + FileOptionsBuilder(const QString &filePath, const CppTools::ProjectPart &projectPart) + : m_filePath(filePath) + , m_projectPart(projectPart) + , m_builder(projectPart) + { + // Determine the driver mode from toolchain and flags. + m_builder.evaluateCompilerFlags(); + m_isClMode = m_builder.isClStyle(); + + addLanguageOptions(); + addGlobalDiagnosticOptions(); // Before addDiagnosticOptions() so users still can overwrite. + addDiagnosticOptions(); + addGlobalOptions(); + addPrecompiledHeaderOptions(); + } + + const QStringList &options() const { return m_options; } + const ::Utils::Id &diagnosticConfigId() const { return m_diagnosticConfigId; } + CppTools::UseBuildSystemWarnings useBuildSystemWarnings() const + { + return m_useBuildSystemWarnings; + } + +private: + void addLanguageOptions() + { + // Determine file kind with respect to ambiguous headers. + CppTools::ProjectFile::Kind fileKind = CppTools::ProjectFile::classify(m_filePath); + if (fileKind == CppTools::ProjectFile::AmbiguousHeader) { + fileKind = m_projectPart.languageVersion <= ::Utils::LanguageVersion::LatestC + ? CppTools::ProjectFile::CHeader + : CppTools::ProjectFile::CXXHeader; + } + + m_builder.reset(); + m_builder.updateFileLanguage(fileKind); + + m_options.append(m_builder.options()); + } + + void addDiagnosticOptions() + { + if (m_projectPart.project) { + ClangProjectSettings &projectSettings = getProjectSettings(m_projectPart.project); + if (!projectSettings.useGlobalConfig()) { + const ::Utils::Id warningConfigId = projectSettings.warningConfigId(); + const CppTools::ClangDiagnosticConfigsModel configsModel + = CppTools::diagnosticConfigsModel(); + if (configsModel.hasConfigWithId(warningConfigId)) { + addDiagnosticOptionsForConfig(configsModel.configWithId(warningConfigId)); + return; + } + } + } + + addDiagnosticOptionsForConfig(CppTools::codeModelSettings()->clangDiagnosticConfig()); + } + + void addDiagnosticOptionsForConfig(const CppTools::ClangDiagnosticConfig &diagnosticConfig) + { + m_diagnosticConfigId = diagnosticConfig.id(); + m_useBuildSystemWarnings = diagnosticConfig.useBuildSystemWarnings() + ? CppTools::UseBuildSystemWarnings::Yes + : CppTools::UseBuildSystemWarnings::No; + + const QStringList options = m_isClMode + ? CppTools::clangArgsForCl(diagnosticConfig.clangOptions()) + : diagnosticConfig.clangOptions(); + m_options.append(options); + } + + void addGlobalDiagnosticOptions() + { + m_options += CppTools::ClangDiagnosticConfigsModel::globalDiagnosticOptions(); + } + + void addGlobalOptions() + { + if (!m_projectPart.project) + m_options.append(ClangProjectSettings::globalCommandLineOptions()); + else + m_options.append(getProjectSettings(m_projectPart.project).commandLineOptions()); + } + + void addPrecompiledHeaderOptions() + { + using namespace CppTools; + + if (getPchUsage() == UsePrecompiledHeaders::No) + return; + + if (m_projectPart.precompiledHeaders.contains(m_filePath)) + return; + + m_builder.reset(); + m_builder.addPrecompiledHeaderOptions(UsePrecompiledHeaders::Yes); + + m_options.append(m_builder.options()); + } + +private: + const QString &m_filePath; + const CppTools::ProjectPart &m_projectPart; + + ::Utils::Id m_diagnosticConfigId; + CppTools::UseBuildSystemWarnings m_useBuildSystemWarnings = CppTools::UseBuildSystemWarnings::No; + CppTools::CompilerOptionsBuilder m_builder; + bool m_isClMode = false; + QStringList m_options; +}; +} // namespace + +QPair<Utils::Id, QStringList> createClangOptions(const CppTools::ProjectPart &projectPart, + const QString &filePath) +{ + QPair<Utils::Id, QStringList> value; + const FileOptionsBuilder fileOptions(filePath, projectPart); + value.first = fileOptions.diagnosticConfigId(); + LibClangOptionsBuilder optionsBuilder(projectPart, fileOptions.useBuildSystemWarnings()); + const QStringList projectPartOptions = optionsBuilder.build(CppTools::ProjectFile::Unsupported, + UsePrecompiledHeaders::No); + value.second = projectPartOptions + fileOptions.options(); + return value; +} + } // namespace Internal } // namespace Clang |