CppTools: Turn some classes into pure value types

ProjectInfo, ProjectPart and ProjectUpdateInfo used to carry pointers
to Project and/or Toolchain, even though they were used in contexts
where these pointers were either unsafe to access or not guaranteed to
be valid anymore, which made their use difficult and error-prone.
We turn these classes into pure value types by copying in all relevant
information before the first async operation takes place.

Fixes: QTCREATORBUG-25678
Change-Id: I1914b0dbda6c7dfba6c95e5e92f2d69977755590
Reviewed-by: Jarek Kobus <jaroslaw.kobus@qt.io>
This commit is contained in:
Christian Kandeler
2021-05-07 16:10:07 +02:00
parent 3143ba79e3
commit 33108795d6
61 changed files with 1086 additions and 958 deletions

View File

@@ -76,10 +76,15 @@ void ClangCodeModelPlugin::generateCompilationDB()
if (!target)
return;
const auto projectInfo = CppModelManager::instance()->projectInfo(target->project());
if (!projectInfo)
return;
QFuture<GenerateCompilationDbResult> task
= QtConcurrent::run(&Internal::generateCompilationDB,
CppModelManager::instance()->projectInfo(target->project()),
CompilationDbPurpose::Project);
= QtConcurrent::run(&Internal::generateCompilationDB, projectInfo,
CompilationDbPurpose::Project,
warningsConfigForProject(target->project()),
optionsForProject(target->project()));
Core::ProgressManager::addTask(task, tr("Generating Compilation DB"), "generate compilation db");
m_generatorWatcher.setFuture(task);
}
@@ -89,8 +94,8 @@ static bool isDBGenerationEnabled(ProjectExplorer::Project *project)
using namespace CppTools;
if (!project)
return false;
ProjectInfo projectInfo = CppModelManager::instance()->projectInfo(project);
return projectInfo.isValid() && !projectInfo.projectParts().isEmpty();
const ProjectInfo::Ptr projectInfo = CppModelManager::instance()->projectInfo(project);
return projectInfo && !projectInfo->projectParts().isEmpty();
}
ClangCodeModelPlugin::~ClangCodeModelPlugin()

View File

@@ -735,9 +735,10 @@ ClangdClient::ClangdClient(Project *project, const Utils::FilePath &jsonDbDir)
setActivateDocumentAutomatically(true);
if (!project) {
QJsonObject initOptions;
const auto clangOptions = createClangOptions(
*CppTools::CppModelManager::instance()->fallbackProjectPart(), {});
initOptions.insert("fallbackFlags", QJsonArray::fromStringList(clangOptions.second));
const QStringList clangOptions = createClangOptions(
*CppTools::CppModelManager::instance()->fallbackProjectPart(), {},
warningsConfigForProject(nullptr), optionsForProject(nullptr));
initOptions.insert("fallbackFlags", QJsonArray::fromStringList(clangOptions));
setInitializationOptions(initOptions);
}
ClientCapabilities caps = Client::defaultClientCapabilities();

View File

@@ -26,6 +26,7 @@
#include "clangeditordocumentprocessor.h"
#include "clangbackendcommunicator.h"
#include "clangprojectsettings.h"
#include "clangdiagnostictooltipwidget.h"
#include "clangfixitoperation.h"
#include "clangfixitoperationsextractor.h"
@@ -463,11 +464,14 @@ void ClangEditorDocumentProcessor::updateBackendDocument(CppTools::ProjectPart &
return;
}
const auto clangOptions = createClangOptions(projectPart, filePath());
m_diagnosticConfigId = clangOptions.first;
ProjectExplorer::Project * const project = CppTools::projectForProjectPart(projectPart);
const CppTools::ClangDiagnosticConfig config = warningsConfigForProject(project);
const QStringList clangOptions = createClangOptions(projectPart, filePath(), config,
optionsForProject(project));
m_diagnosticConfigId = config.id();
m_communicator.documentsOpened(
{fileContainerWithOptionsAndDocumentContent(clangOptions.second, projectPart.headerPaths)});
{fileContainerWithOptionsAndDocumentContent(clangOptions, projectPart.headerPaths)});
setLastSentDocumentRevision(filePath(), revision());
}

View File

@@ -48,7 +48,6 @@
#include <cpptools/cppmodelmanager.h>
#include <cpptools/cpptoolsreuse.h>
#include <cpptools/editordocumenthandle.h>
#include <cpptools/projectinfo.h>
#include <languageclient/languageclientmanager.h>
@@ -275,7 +274,7 @@ void ClangModelManagerSupport::connectToWidgetsMarkContextMenuRequested(QWidget
}
void ClangModelManagerSupport::updateLanguageClient(ProjectExplorer::Project *project,
const CppTools::ProjectInfo &projectInfo)
const CppTools::ProjectInfo::Ptr &projectInfo)
{
if (!CppTools::ClangdProjectSettings(project).settings().useClangd)
return;
@@ -300,7 +299,8 @@ void ClangModelManagerSupport::updateLanguageClient(ProjectExplorer::Project *pr
return;
if (!CppTools::ClangdProjectSettings(project).settings().useClangd)
return;
if (cppModelManager()->projectInfo(project) != projectInfo)
const CppTools::ProjectInfo::Ptr newProjectInfo = cppModelManager()->projectInfo(project);
if (!newProjectInfo || *newProjectInfo != *projectInfo)
return;
if (getJsonDbDir() != jsonDbDir)
return;
@@ -320,7 +320,9 @@ void ClangModelManagerSupport::updateLanguageClient(ProjectExplorer::Project *pr
return;
if (!CppTools::ClangdProjectSettings(project).settings().useClangd)
return;
if (cppModelManager()->projectInfo(project) != projectInfo)
const CppTools::ProjectInfo::Ptr newProjectInfo
= cppModelManager()->projectInfo(project);
if (!newProjectInfo || *newProjectInfo != *projectInfo)
return;
// Acquaint the client with all open C++ documents for this project.
@@ -361,7 +363,9 @@ void ClangModelManagerSupport::updateLanguageClient(ProjectExplorer::Project *pr
});
auto future = Utils::runAsync(&Internal::generateCompilationDB, projectInfo,
CompilationDbPurpose::CodeModel);
CompilationDbPurpose::CodeModel,
warningsConfigForProject(project),
optionsForProject(project));
generatorWatcher->setFuture(future);
m_generatorSynchronizer.addFuture(future);
}
@@ -576,7 +580,7 @@ static ClangEditorDocumentProcessors
clangProcessorsWithProject(const ProjectExplorer::Project *project)
{
return ::Utils::filtered(clangProcessors(), [project](ClangEditorDocumentProcessor *p) {
return p->hasProjectPart() && p->projectPart()->project == project;
return p->hasProjectPart() && p->projectPart()->belongsToProject(project);
});
}
@@ -611,13 +615,13 @@ void ClangModelManagerSupport::onAboutToRemoveProject(ProjectExplorer::Project *
void ClangModelManagerSupport::onProjectPartsUpdated(ProjectExplorer::Project *project)
{
QTC_ASSERT(project, return);
const CppTools::ProjectInfo projectInfo = cppModelManager()->projectInfo(project);
QTC_ASSERT(projectInfo.isValid(), return);
const CppTools::ProjectInfo::Ptr projectInfo = cppModelManager()->projectInfo(project);
QTC_ASSERT(projectInfo, return);
updateLanguageClient(project, projectInfo);
QStringList projectPartIds;
for (const CppTools::ProjectPart::Ptr &projectPart : projectInfo.projectParts())
for (const CppTools::ProjectPart::Ptr &projectPart : projectInfo->projectParts())
projectPartIds.append(projectPart->id());
onProjectPartsRemoved(projectPartIds);
}

View File

@@ -29,6 +29,7 @@
#include "clanguiheaderondiskmanager.h"
#include <cpptools/cppmodelmanagersupport.h>
#include <cpptools/projectinfo.h>
#include <utils/futuresynchronizer.h>
#include <utils/id.h>
@@ -129,7 +130,7 @@ private:
void connectToWidgetsMarkContextMenuRequested(QWidget *editorWidget);
void updateLanguageClient(ProjectExplorer::Project *project,
const CppTools::ProjectInfo &projectInfo);
const CppTools::ProjectInfo::Ptr &projectInfo);
ClangdClient *createClient(ProjectExplorer::Project *project, const Utils::FilePath &jsonDbDir);
void claimNonProjectSources(ClangdClient *fallbackClient);

View File

@@ -88,7 +88,7 @@ ProjectExplorer::Project *projectForCurrentEditor()
if (auto processor = ClangEditorDocumentProcessor::get(filePath)) {
if (ProjectPart::Ptr projectPart = processor->projectPart())
return projectPart->project;
return projectForProjectPart(*projectPart);
}
return nullptr;

View File

@@ -296,30 +296,10 @@ QString diagnosticCategoryPrefixRemoved(const QString &text)
return text;
}
static FilePath compilerPath(const CppTools::ProjectPart &projectPart)
{
Target *target = projectPart.project->activeTarget();
if (!target)
return FilePath();
ToolChain *toolchain = ToolChainKitAspect::cxxToolChain(target->kit());
return toolchain->compilerCommand();
}
static FilePath buildDirectory(const ProjectExplorer::Project &project)
{
if (auto *target = project.activeTarget()) {
if (auto *bc = target->activeBuildConfiguration())
return bc->buildDirectory();
}
return {};
}
static QStringList projectPartArguments(const ProjectPart &projectPart)
{
QStringList args;
args << compilerPath(projectPart).toString();
args << projectPart.compilerFilePath.toString();
args << "-c";
if (projectPart.toolchainType != ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID) {
args << "--target=" + projectPart.toolChainTargetTriple;
@@ -350,7 +330,9 @@ static QJsonObject createFileObject(const FilePath &buildDir,
const QStringList &arguments,
const ProjectPart &projectPart,
const ProjectFile &projFile,
CompilationDbPurpose purpose)
CompilationDbPurpose purpose,
const ClangDiagnosticConfig &warningsConfig,
const QStringList &projectOptions)
{
QJsonObject fileObject;
fileObject["file"] = projFile.path;
@@ -376,7 +358,8 @@ static QJsonObject createFileObject(const FilePath &buildDir,
}
} else {
// TODO: Do we really need to re-calculate the project part options per source file?
args = QJsonArray::fromStringList(createClangOptions(projectPart, projFile.path).second);
args = QJsonArray::fromStringList(createClangOptions(projectPart, projFile.path,
warningsConfig, projectOptions));
args.prepend("clang"); // TODO: clang-cl for MSVC targets? Does it matter at all what we put here?
}
@@ -386,10 +369,12 @@ static QJsonObject createFileObject(const FilePath &buildDir,
return fileObject;
}
GenerateCompilationDbResult generateCompilationDB(CppTools::ProjectInfo projectInfo,
CompilationDbPurpose purpose)
GenerateCompilationDbResult generateCompilationDB(const CppTools::ProjectInfo::Ptr projectInfo,
CompilationDbPurpose purpose,
const ClangDiagnosticConfig &warningsConfig,
const QStringList &projectOptions)
{
const FilePath buildDir = buildDirectory(*projectInfo.project());
const FilePath buildDir = projectInfo->buildRoot();
QTC_ASSERT(!buildDir.isEmpty(), return GenerateCompilationDbResult(QString(),
QCoreApplication::translate("ClangUtils", "Could not retrieve build directory.")));
@@ -405,13 +390,13 @@ GenerateCompilationDbResult generateCompilationDB(CppTools::ProjectInfo projectI
}
compileCommandsFile.write("[");
for (ProjectPart::Ptr projectPart : projectInfo.projectParts()) {
for (ProjectPart::Ptr projectPart : projectInfo->projectParts()) {
QStringList args;
if (purpose == CompilationDbPurpose::Project)
args = projectPartArguments(*projectPart);
for (const ProjectFile &projFile : projectPart->files) {
const QJsonObject json = createFileObject(buildDir, args, *projectPart, projFile,
purpose);
purpose, warningsConfig, projectOptions);
if (compileCommandsFile.size() > 1)
compileCommandsFile.write(",");
compileCommandsFile.write('\n' + QJsonDocument(json).toJson().trimmed());
@@ -495,9 +480,12 @@ static ClangProjectSettings &getProjectSettings(ProjectExplorer::Project *projec
class FileOptionsBuilder
{
public:
FileOptionsBuilder(const QString &filePath, const CppTools::ProjectPart &projectPart)
FileOptionsBuilder(const QString &filePath, const CppTools::ProjectPart &projectPart,
const ClangDiagnosticConfig &warningsConfig,
const QStringList &projectOptions)
: m_filePath(filePath)
, m_projectPart(projectPart)
, m_warningsConfig(warningsConfig)
, m_builder(projectPart)
{
// Determine the driver mode from toolchain and flags.
@@ -507,12 +495,11 @@ public:
addLanguageOptions();
addGlobalDiagnosticOptions(); // Before addDiagnosticOptions() so users still can overwrite.
addDiagnosticOptions();
addGlobalOptions();
m_options.append(projectOptions);
addPrecompiledHeaderOptions();
}
const QStringList &options() const { return m_options; }
const ::Utils::Id &diagnosticConfigId() const { return m_diagnosticConfigId; }
CppTools::UseBuildSystemWarnings useBuildSystemWarnings() const
{
return m_useBuildSystemWarnings;
@@ -539,25 +526,11 @@ private:
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());
addDiagnosticOptionsForConfig(m_warningsConfig);
}
void addDiagnosticOptionsForConfig(const CppTools::ClangDiagnosticConfig &diagnosticConfig)
{
m_diagnosticConfigId = diagnosticConfig.id();
m_useBuildSystemWarnings = diagnosticConfig.useBuildSystemWarnings()
? CppTools::UseBuildSystemWarnings::Yes
: CppTools::UseBuildSystemWarnings::No;
@@ -573,14 +546,6 @@ private:
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;
@@ -600,8 +565,8 @@ private:
private:
const QString &m_filePath;
const CppTools::ProjectPart &m_projectPart;
const ClangDiagnosticConfig &m_warningsConfig;
::Utils::Id m_diagnosticConfigId;
CppTools::UseBuildSystemWarnings m_useBuildSystemWarnings = CppTools::UseBuildSystemWarnings::No;
CppTools::CompilerOptionsBuilder m_builder;
bool m_isClMode = false;
@@ -609,17 +574,38 @@ private:
};
} // namespace
QPair<Utils::Id, QStringList> createClangOptions(const CppTools::ProjectPart &projectPart,
const QString &filePath)
QStringList createClangOptions(const ProjectPart &projectPart, const QString &filePath,
const ClangDiagnosticConfig &warningsConfig,
const QStringList &projectOptions)
{
QPair<Utils::Id, QStringList> value;
const FileOptionsBuilder fileOptions(filePath, projectPart);
value.first = fileOptions.diagnosticConfigId();
const FileOptionsBuilder fileOptions(filePath, projectPart, warningsConfig, projectOptions);
LibClangOptionsBuilder optionsBuilder(projectPart, fileOptions.useBuildSystemWarnings());
const QStringList projectPartOptions = optionsBuilder.build(CppTools::ProjectFile::Unsupported,
UsePrecompiledHeaders::No);
value.second = projectPartOptions + fileOptions.options();
return value;
return projectPartOptions + fileOptions.options();
}
ClangDiagnosticConfig warningsConfigForProject(Project *project)
{
if (project) {
ClangProjectSettings &projectSettings = ClangModelManagerSupport::instance()
->projectSettings(project);
if (!projectSettings.useGlobalConfig()) {
const Utils::Id warningConfigId = projectSettings.warningConfigId();
const CppTools::ClangDiagnosticConfigsModel configsModel
= CppTools::diagnosticConfigsModel();
if (configsModel.hasConfigWithId(warningConfigId))
return configsModel.configWithId(warningConfigId);
}
}
return CppTools::codeModelSettings()->clangDiagnosticConfig();
}
const QStringList optionsForProject(ProjectExplorer::Project *project)
{
if (project)
return getProjectSettings(project).commandLineOptions();
return ClangProjectSettings::globalCommandLineOptions();
}
} // namespace Internal

View File

@@ -27,7 +27,7 @@
#include <cplusplus/Icons.h>
#include <cpptools/projectpart.h>
#include <cpptools/projectinfo.h>
#include <cpptools/compileroptionsbuilder.h>
#include <QPair>
@@ -38,20 +38,26 @@ class QTextBlock;
QT_END_NAMESPACE
namespace CppTools {
class ClangDiagnosticConfig;
class CppEditorDocumentHandle;
class ProjectInfo;
}
namespace ClangBackEnd { class TokenInfoContainer; }
namespace ProjectExplorer { class Project; }
namespace ClangCodeModel {
namespace Internal {
CppTools::CppEditorDocumentHandle *cppDocument(const QString &filePath);
void setLastSentDocumentRevision(const QString &filePath, uint revision);
QPair<Utils::Id, QStringList> createClangOptions(const CppTools::ProjectPart &projectPart,
const QString &filePath);
CppTools::ClangDiagnosticConfig warningsConfigForProject(ProjectExplorer::Project *project);
const QStringList optionsForProject(ProjectExplorer::Project *project);
QStringList createClangOptions(const CppTools::ProjectPart &projectPart, const QString &filePath,
const CppTools::ClangDiagnosticConfig &warningsConfig,
const QStringList &projectOptions);
CppTools::ProjectPart::Ptr projectPartForFile(const QString &filePath);
CppTools::ProjectPart::Ptr projectPartForFileBasedOnProcessor(const QString &filePath);
@@ -79,8 +85,9 @@ public:
};
enum class CompilationDbPurpose { Project, CodeModel };
GenerateCompilationDbResult generateCompilationDB(CppTools::ProjectInfo projectInfo,
CompilationDbPurpose purpose);
GenerateCompilationDbResult generateCompilationDB(const CppTools::ProjectInfo::Ptr projectInfo,
CompilationDbPurpose purpose, const CppTools::ClangDiagnosticConfig &warningsConfig,
const QStringList &projectOptions);
class DiagnosticTextInfo
{

View File

@@ -246,31 +246,33 @@ bool OpenEditorAtCursorPosition::waitUntil(const std::function<bool ()> &conditi
return false;
}
CppTools::ProjectPart::Ptr createProjectPart(const QStringList &files,
CppTools::ProjectPart::Ptr createProjectPart(const Utils::FilePath &projectFilePath,
const QStringList &files,
const ProjectExplorer::Macros &macros)
{
using namespace CppTools;
ProjectPart::Ptr projectPart(new ProjectPart);
projectPart->projectFile = QLatin1String("myproject.project");
foreach (const QString &file, files)
projectPart->files.append(ProjectFile(file, ProjectFile::classify(file)));
projectPart->qtVersion = ::Utils::QtVersion::None;
projectPart->projectMacros = macros;
return projectPart;
ProjectExplorer::RawProjectPart rpp;
rpp.setProjectFileLocation("myproject.project");
rpp.setQtVersion(Utils::QtVersion::None);
rpp.setMacros(macros);
const auto projectFiles = Utils::transform<ProjectFiles>(files, [](const QString &f) {
return ProjectFile(f, ProjectFile::classify(f));
});
return ProjectPart::create(projectFilePath, rpp, {}, projectFiles);
}
CppTools::ProjectInfo createProjectInfo(ProjectExplorer::Project *project,
const QStringList &files,
const ProjectExplorer::Macros &macros)
CppTools::ProjectInfo::Ptr createProjectInfo(ProjectExplorer::Project *project,
const QStringList &files,
const ProjectExplorer::Macros &macros)
{
using namespace CppTools;
QTC_ASSERT(project, return ProjectInfo());
QTC_ASSERT(project, return {});
const CppTools::ProjectPart::Ptr projectPart = createProjectPart(files, macros);
ProjectInfo projectInfo = ProjectInfo(project);
projectInfo.appendProjectPart(projectPart);
const CppTools::ProjectPart::Ptr projectPart = createProjectPart(project->projectFilePath(),
files, macros);
const auto projectInfo = ProjectInfo::create(
{project, ProjectExplorer::KitInfo(nullptr), {}, {}}, {projectPart});
return projectInfo;
}
@@ -290,9 +292,9 @@ public:
bool load()
{
m_project = m_helper.createProject(QLatin1String("testProject"));
const CppTools::ProjectInfo projectInfo = createProjectInfo(m_project,
m_projectFiles,
m_projectMacros);
const CppTools::ProjectInfo::Ptr projectInfo = createProjectInfo(m_project,
m_projectFiles,
m_projectMacros);
const QSet<QString> filesIndexedAfterLoading = m_helper.updateProjectInfo(projectInfo);
return m_projectFiles.size() == filesIndexedAfterLoading.size();
}
@@ -300,15 +302,14 @@ public:
bool updateProject(const ProjectExplorer::Macros &updatedProjectMacros)
{
QTC_ASSERT(m_project, return false);
const CppTools::ProjectInfo updatedProjectInfo = createProjectInfo(m_project,
m_projectFiles,
updatedProjectMacros);
const CppTools::ProjectInfo::Ptr updatedProjectInfo
= createProjectInfo(m_project, m_projectFiles, updatedProjectMacros);
return updateProjectInfo(updatedProjectInfo);
}
private:
bool updateProjectInfo(const CppTools::ProjectInfo &projectInfo)
bool updateProjectInfo(const CppTools::ProjectInfo::Ptr &projectInfo)
{
const QSet<QString> filesIndexedAfterLoading = m_helper.updateProjectInfo(projectInfo);
return m_projectFiles.size() == filesIndexedAfterLoading.size();
@@ -720,8 +721,7 @@ void ClangCodeCompletionTest::testCompleteProjectDependingCodeInGeneratedUiFile(
// Open project
const QString projectFilePath = testDir.absolutePath("qt-widgets-app.pro");
CppTools::Tests::ProjectOpenerAndCloser projectManager;
const CppTools::ProjectInfo projectInfo = projectManager.open(projectFilePath, true);
QVERIFY(projectInfo.isValid());
QVERIFY(projectManager.open(projectFilePath, true));
QVERIFY(monitorGeneratedUiFile.waitUntilGenerated());
// Open file with ui object