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

@@ -153,7 +153,7 @@ public:
// Project integration
mutable QMutex m_projectMutex;
QMap<ProjectExplorer::Project *, ProjectInfo> m_projectToProjectsInfo;
QMap<ProjectExplorer::Project *, ProjectInfo::Ptr> m_projectToProjectsInfo;
QHash<ProjectExplorer::Project *, bool> m_projectToIndexerCanceled;
QMap<Utils::FilePath, QList<ProjectPart::Ptr> > m_fileToProjectParts;
QMap<QString, ProjectPart::Ptr> m_projectPartIdToProjectProjectPart;
@@ -748,8 +748,8 @@ void CppModelManager::ensureUpdated()
QStringList CppModelManager::internalProjectFiles() const
{
QStringList files;
for (const ProjectInfo &pinfo : qAsConst(d->m_projectToProjectsInfo)) {
foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) {
for (const ProjectInfo::Ptr &pinfo : qAsConst(d->m_projectToProjectsInfo)) {
foreach (const ProjectPart::Ptr &part, pinfo->projectParts()) {
foreach (const ProjectFile &file, part->files)
files += file.path;
}
@@ -761,8 +761,8 @@ QStringList CppModelManager::internalProjectFiles() const
ProjectExplorer::HeaderPaths CppModelManager::internalHeaderPaths() const
{
ProjectExplorer::HeaderPaths headerPaths;
for (const ProjectInfo &pinfo : qAsConst(d->m_projectToProjectsInfo)) {
foreach (const ProjectPart::Ptr &part, pinfo.projectParts()) {
for (const ProjectInfo::Ptr &pinfo : qAsConst(d->m_projectToProjectsInfo)) {
foreach (const ProjectPart::Ptr &part, pinfo->projectParts()) {
foreach (const ProjectExplorer::HeaderPath &path, part->headerPaths) {
ProjectExplorer::HeaderPath hp(QDir::cleanPath(path.path), path.type);
if (!headerPaths.contains(hp))
@@ -789,8 +789,8 @@ ProjectExplorer::Macros CppModelManager::internalDefinedMacros() const
{
ProjectExplorer::Macros macros;
QSet<ProjectExplorer::Macro> alreadyIn;
for (const ProjectInfo &pinfo : qAsConst(d->m_projectToProjectsInfo)) {
for (const ProjectPart::Ptr &part : pinfo.projectParts()) {
for (const ProjectInfo::Ptr &pinfo : qAsConst(d->m_projectToProjectsInfo)) {
for (const ProjectPart::Ptr &part : pinfo->projectParts()) {
addUnique(part->toolChainMacros, macros, alreadyIn);
addUnique(part->projectMacros, macros, alreadyIn);
}
@@ -972,24 +972,21 @@ QFuture<void> CppModelManager::updateSourceFiles(const QSet<QString> &sourceFile
return d->m_internalIndexingSupport->refreshSourceFiles(filteredFiles, mode);
}
QList<ProjectInfo> CppModelManager::projectInfos() const
QList<ProjectInfo::Ptr> CppModelManager::projectInfos() const
{
QMutexLocker locker(&d->m_projectMutex);
return d->m_projectToProjectsInfo.values();
}
ProjectInfo CppModelManager::projectInfo(ProjectExplorer::Project *project) const
ProjectInfo::Ptr CppModelManager::projectInfo(ProjectExplorer::Project *project) const
{
QMutexLocker locker(&d->m_projectMutex);
return d->m_projectToProjectsInfo.value(project, ProjectInfo());
return d->m_projectToProjectsInfo.value(project);
}
/// \brief Remove all files and their includes (recursively) of given ProjectInfo from the snapshot.
void CppModelManager::removeProjectInfoFilesAndIncludesFromSnapshot(const ProjectInfo &projectInfo)
{
if (!projectInfo.isValid())
return;
QMutexLocker snapshotLocker(&d->m_snapshotMutex);
foreach (const ProjectPart::Ptr &projectPart, projectInfo.projectParts()) {
foreach (const ProjectFile &cxxFile, projectPart->files) {
@@ -1089,8 +1086,8 @@ void CppModelManager::recalculateProjectPartMappings()
{
d->m_projectPartIdToProjectProjectPart.clear();
d->m_fileToProjectParts.clear();
foreach (const ProjectInfo &projectInfo, d->m_projectToProjectsInfo) {
foreach (const ProjectPart::Ptr &projectPart, projectInfo.projectParts()) {
foreach (const ProjectInfo::Ptr &projectInfo, d->m_projectToProjectsInfo) {
foreach (const ProjectPart::Ptr &projectPart, projectInfo->projectParts()) {
d->m_projectPartIdToProjectProjectPart[projectPart->id()] = projectPart;
foreach (const ProjectFile &cxxFile, projectPart->files)
d->m_fileToProjectParts[Utils::FilePath::fromString(cxxFile.path)].append(
@@ -1152,37 +1149,37 @@ void CppModelManager::updateCppEditorDocuments(bool projectsUpdated) const
}
}
QFuture<void> CppModelManager::updateProjectInfo(const ProjectInfo &newProjectInfo,
QFuture<void> CppModelManager::updateProjectInfo(const ProjectInfo::Ptr &newProjectInfo,
const QSet<QString> &additionalFiles)
{
if (!newProjectInfo.isValid())
return QFuture<void>();
ProjectInfo theNewProjectInfo = newProjectInfo;
theNewProjectInfo.finish();
if (!newProjectInfo)
return {};
QSet<QString> filesToReindex;
QStringList removedProjectParts;
bool filesRemoved = false;
ProjectExplorer::Project *project = theNewProjectInfo.project().data();
ProjectExplorer::Project * const project = projectForProjectInfo(*newProjectInfo);
if (!project)
return {};
{ // Only hold the mutex for a limited scope, so the dumping afterwards does not deadlock.
QMutexLocker projectLocker(&d->m_projectMutex);
const QSet<QString> newSourceFiles = theNewProjectInfo.sourceFiles();
const QSet<QString> newSourceFiles = newProjectInfo->sourceFiles();
// Check if we can avoid a full reindexing
ProjectInfo oldProjectInfo = d->m_projectToProjectsInfo.value(project);
const ProjectInfo::Ptr oldProjectInfo = d->m_projectToProjectsInfo.value(project);
const bool previousIndexerCanceled = d->m_projectToIndexerCanceled.value(project, false);
if (!previousIndexerCanceled && oldProjectInfo.isValid()) {
ProjectInfoComparer comparer(oldProjectInfo, theNewProjectInfo);
if (!previousIndexerCanceled && oldProjectInfo) {
ProjectInfoComparer comparer(*oldProjectInfo, *newProjectInfo);
if (comparer.configurationOrFilesChanged()) {
d->m_dirty = true;
// If the project configuration changed, do a full reindexing
if (comparer.configurationChanged()) {
removeProjectInfoFilesAndIncludesFromSnapshot(oldProjectInfo);
removeProjectInfoFilesAndIncludesFromSnapshot(*oldProjectInfo);
filesToReindex.unite(newSourceFiles);
// The "configuration file" includes all defines and therefore should be updated
@@ -1218,7 +1215,7 @@ QFuture<void> CppModelManager::updateProjectInfo(const ProjectInfo &newProjectIn
}
// Update Project/ProjectInfo and File/ProjectPart table
d->m_projectToProjectsInfo.insert(project, theNewProjectInfo);
d->m_projectToProjectsInfo.insert(project, newProjectInfo);
recalculateProjectPartMappings();
} // Mutex scope
@@ -1236,7 +1233,7 @@ QFuture<void> CppModelManager::updateProjectInfo(const ProjectInfo &newProjectIn
emit projectPartsRemoved(removedProjectParts);
// Announce added project parts
emit projectPartsUpdated(theNewProjectInfo.project().data());
emit projectPartsUpdated(project);
// Ideally, we would update all the editor documents that depend on the 'filesToReindex'.
// However, on e.g. a session restore first the editor documents are created and then the
@@ -1531,18 +1528,16 @@ void CppModelManager::onCoreAboutToClose()
void CppModelManager::setupFallbackProjectPart()
{
ProjectPart::Ptr part(new ProjectPart);
part->projectMacros = definedMacros();
part->headerPaths = headerPaths();
ToolChainInfo tcInfo;
RawProjectPart rpp;
rpp.setMacros(definedMacros());
rpp.setHeaderPaths(headerPaths());
rpp.setQtVersion(Utils::QtVersion::Qt5);
// Do not activate ObjectiveCExtensions since this will lead to the
// "objective-c++" language option for a project-less *.cpp file.
part->languageExtensions = Utils::LanguageExtension::All;
part->languageExtensions &= ~Utils::LanguageExtensions(
Utils::LanguageExtension::ObjectiveC);
part->qtVersion = Utils::QtVersion::Qt5;
Utils::LanguageExtensions langExtensions = Utils::LanguageExtension::All;
langExtensions &= ~Utils::LanguageExtensions(Utils::LanguageExtension::ObjectiveC);
// TODO: Use different fallback toolchain for different kinds of files?
const Kit * const defaultKit = KitManager::isLoaded() ? KitManager::defaultKit() : nullptr;
@@ -1553,15 +1548,17 @@ void CppModelManager::setupFallbackProjectPart()
if (sysroot.isEmpty())
sysroot = Utils::FilePath::fromString(defaultTc->sysRoot());
Utils::Environment env = defaultKit->buildEnvironment();
ToolChainInfo tcInfo(defaultTc, sysroot.toString(), env);
part->setupToolchainProperties(tcInfo, {});
if (part->language == Language::C)
part->languageVersion = Utils::LanguageVersion::LatestC;
else
part->languageVersion = Utils::LanguageVersion::LatestCxx;
tcInfo = ToolChainInfo(defaultTc, sysroot.toString(), env);
const auto macroInspectionWrapper = [runner = tcInfo.macroInspectionRunner](
const QStringList &flags) {
ToolChain::MacroInspectionReport report = runner(flags);
report.languageVersion = Utils::LanguageVersion::LatestCxx;
return report;
};
tcInfo.macroInspectionRunner = macroInspectionWrapper;
}
part->updateLanguageFeatures();
const auto part = ProjectPart::create({}, rpp, {}, {}, {}, langExtensions, {}, tcInfo);
QMutexLocker locker(&d->m_fallbackProjectPartMutex);
d->m_fallbackProjectPart = part;
}