From 41a003c4f1f2879579d4df9afb6d5ee660170757 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 26 Feb 2024 17:39:10 -0800 Subject: [PATCH 01/12] Create helper type to store filepath map --- .../Solution/SolutionCompilationState.cs | 2 +- .../Workspace/Solution/SolutionState.cs | 84 +++++++++++++++---- 2 files changed, 70 insertions(+), 16 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs index 1a71118b9cebc..b25a591d8c853 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs @@ -1113,7 +1113,7 @@ private SolutionCompilationState ComputeFrozenSnapshot(CancellationToken cancell var newIdToProjectStateMap = newIdToProjectStateMapBuilder.ToImmutable(); var newIdToTrackerMap = newIdToTrackerMapBuilder.ToImmutable(); - var filePathToDocumentIdsMap = filePathToDocumentIdsMapChanged + FilePathToDocumentIdsMap? filePathToDocumentIdsMap = filePathToDocumentIdsMapChanged ? filePathToDocumentIdsMapBuilder.ToImmutable() : null; diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs index 5e26a3538c837..ff1ab471ec0a2 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs @@ -24,6 +24,60 @@ internal readonly record struct StateChange( ProjectState OldProjectState, ProjectState NewProjectState); + internal readonly struct FilePathToDocumentIdsMap + { + private static readonly ImmutableDictionary> s_emptyMap + = ImmutableDictionary.Create>(StringComparer.OrdinalIgnoreCase); + public static readonly FilePathToDocumentIdsMap Empty = new(s_emptyMap); + + private readonly ImmutableDictionary> _map; + + private FilePathToDocumentIdsMap(ImmutableDictionary> map) + { + _map = map; + } + + public bool TryGetValue(string filePath, out ImmutableArray documentIdsWithPath) + => _map.TryGetValue(filePath, out documentIdsWithPath); + + public static bool operator ==(FilePathToDocumentIdsMap left, FilePathToDocumentIdsMap right) + => left._map == right._map; + + public static bool operator !=(FilePathToDocumentIdsMap left, FilePathToDocumentIdsMap right) + => !(left == right); + + public override int GetHashCode() + => throw new NotSupportedException(); + + public override bool Equals([NotNullWhen(true)] object? obj) + => obj is FilePathToDocumentIdsMap map && Equals(map); + + public Builder ToBuilder() + => new(_map.ToBuilder()); + + public readonly struct Builder + { + private readonly ImmutableDictionary>.Builder _builder; + + public Builder(ImmutableDictionary>.Builder builder) + { + _builder = builder; + } + + public FilePathToDocumentIdsMap ToImmutable() + => new(_builder.ToImmutable()); + + public bool TryGetValue(string filePath, out ImmutableArray documentIdsWithPath) + => _builder.TryGetValue(filePath, out documentIdsWithPath); + + public void MultiAdd(string filePath, DocumentId documentId) + => _builder.MultiAdd(filePath, documentId); + + public void MultiRemove(string filePath, DocumentId documentId) + => _builder.MultiRemove(filePath, documentId); + } + } + /// /// Represents a set of projects and their source code documents. /// @@ -41,7 +95,7 @@ internal sealed partial class SolutionState private readonly SolutionInfo.SolutionAttributes _solutionAttributes; private readonly ImmutableDictionary _projectIdToProjectStateMap; - private readonly ImmutableDictionary> _filePathToDocumentIdsMap; + private readonly FilePathToDocumentIdsMap _filePathToDocumentIdsMap; private readonly ProjectDependencyGraph _dependencyGraph; // holds on data calculated based on the AnalyzerReferences list @@ -56,7 +110,7 @@ private SolutionState( SolutionOptionSet options, IReadOnlyList analyzerReferences, ImmutableDictionary idToProjectStateMap, - ImmutableDictionary> filePathToDocumentIdsMap, + FilePathToDocumentIdsMap filePathToDocumentIdsMap, ProjectDependencyGraph dependencyGraph, Lazy? lazyAnalyzers) { @@ -97,7 +151,7 @@ public SolutionState( options, analyzerReferences, idToProjectStateMap: ImmutableDictionary.Empty, - filePathToDocumentIdsMap: ImmutableDictionary.Create>(StringComparer.OrdinalIgnoreCase), + filePathToDocumentIdsMap: FilePathToDocumentIdsMap.Empty, dependencyGraph: ProjectDependencyGraph.Empty, lazyAnalyzers: null) { @@ -149,7 +203,7 @@ internal SolutionState Branch( SolutionOptionSet? options = null, IReadOnlyList? analyzerReferences = null, ImmutableDictionary? idToProjectStateMap = null, - ImmutableDictionary>? filePathToDocumentIdsMap = null, + FilePathToDocumentIdsMap? filePathToDocumentIdsMap = null, ProjectDependencyGraph? dependencyGraph = null) { solutionAttributes ??= _solutionAttributes; @@ -182,7 +236,7 @@ internal SolutionState Branch( options, analyzerReferences, idToProjectStateMap, - filePathToDocumentIdsMap, + filePathToDocumentIdsMap.Value, dependencyGraph, analyzerReferencesEqual ? _lazyAnalyzers : null); } @@ -220,7 +274,7 @@ public SolutionState WithNewWorkspace( _lazyAnalyzers); } - public ImmutableDictionary> FilePathToDocumentIdsMap => _filePathToDocumentIdsMap; + public FilePathToDocumentIdsMap FilePathToDocumentIdsMap => _filePathToDocumentIdsMap; /// /// The version of the most recently modified project. @@ -406,20 +460,20 @@ public SolutionState RemoveProject(ProjectId projectId) dependencyGraph: newDependencyGraph); } - public ImmutableDictionary> CreateFilePathToDocumentIdsMapWithAddedDocuments(IEnumerable documentStates) + public FilePathToDocumentIdsMap CreateFilePathToDocumentIdsMapWithAddedDocuments(IEnumerable documentStates) { var builder = _filePathToDocumentIdsMap.ToBuilder(); AddDocumentFilePaths(documentStates, builder); return builder.ToImmutable(); } - private static void AddDocumentFilePaths(IEnumerable documentStates, ImmutableDictionary>.Builder builder) + private static void AddDocumentFilePaths(IEnumerable documentStates, FilePathToDocumentIdsMap.Builder builder) { foreach (var documentState in documentStates) AddDocumentFilePath(documentState, builder); } - public static void AddDocumentFilePath(TextDocumentState documentState, ImmutableDictionary>.Builder builder) + public static void AddDocumentFilePath(TextDocumentState documentState, FilePathToDocumentIdsMap.Builder builder) { var filePath = documentState.FilePath; @@ -429,20 +483,20 @@ public static void AddDocumentFilePath(TextDocumentState documentState, Immutabl builder.MultiAdd(filePath, documentState.Id); } - public ImmutableDictionary> CreateFilePathToDocumentIdsMapWithRemovedDocuments(IEnumerable documentStates) + public FilePathToDocumentIdsMap CreateFilePathToDocumentIdsMapWithRemovedDocuments(IEnumerable documentStates) { var builder = _filePathToDocumentIdsMap.ToBuilder(); RemoveDocumentFilePaths(documentStates, builder); return builder.ToImmutable(); } - private static void RemoveDocumentFilePaths(IEnumerable documentStates, ImmutableDictionary>.Builder builder) + private static void RemoveDocumentFilePaths(IEnumerable documentStates, FilePathToDocumentIdsMap.Builder builder) { foreach (var documentState in documentStates) RemoveDocumentFilePath(documentState, builder); } - public static void RemoveDocumentFilePath(TextDocumentState documentState, ImmutableDictionary>.Builder builder) + public static void RemoveDocumentFilePath(TextDocumentState documentState, FilePathToDocumentIdsMap.Builder builder) { var filePath = documentState.FilePath; if (RoslynString.IsNullOrEmpty(filePath)) @@ -454,7 +508,7 @@ public static void RemoveDocumentFilePath(TextDocumentState documentState, Immut builder.MultiRemove(filePath, documentState.Id); } - private ImmutableDictionary> CreateFilePathToDocumentIdsMapWithFilePath(DocumentId documentId, string? oldFilePath, string? newFilePath) + private FilePathToDocumentIdsMap CreateFilePathToDocumentIdsMapWithFilePath(DocumentId documentId, string? oldFilePath, string? newFilePath) { if (oldFilePath == newFilePath) { @@ -1185,7 +1239,7 @@ public StateChange ForkProject( ProjectState oldProjectState, ProjectState newProjectState, ProjectDependencyGraph? newDependencyGraph = null, - ImmutableDictionary>? newFilePathToDocumentIdsMap = null) + FilePathToDocumentIdsMap? newFilePathToDocumentIdsMap = null) { var projectId = newProjectState.Id; @@ -1213,7 +1267,7 @@ public ImmutableArray GetDocumentIdsWithFilePath(string? filePath) return []; } - return _filePathToDocumentIdsMap.TryGetValue(filePath!, out var documentIds) + return _filePathToDocumentIdsMap.TryGetValue(filePath, out var documentIds) ? documentIds : []; } From 7fbf4bbf9083a1c44ac9ba55aae6bce7e8ed585f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 26 Feb 2024 17:39:29 -0800 Subject: [PATCH 02/12] Move helper type to its own file --- .../Solution/FilePathToDocumentIdsMap.cs | 65 +++++++++++++++++++ .../Workspace/Solution/SolutionState.cs | 54 --------------- 2 files changed, 65 insertions(+), 54 deletions(-) create mode 100644 src/Workspaces/Core/Portable/Workspace/Solution/FilePathToDocumentIdsMap.cs diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/FilePathToDocumentIdsMap.cs b/src/Workspaces/Core/Portable/Workspace/Solution/FilePathToDocumentIdsMap.cs new file mode 100644 index 0000000000000..58316915903df --- /dev/null +++ b/src/Workspaces/Core/Portable/Workspace/Solution/FilePathToDocumentIdsMap.cs @@ -0,0 +1,65 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis +{ + internal readonly struct FilePathToDocumentIdsMap + { + private static readonly ImmutableDictionary> s_emptyMap + = ImmutableDictionary.Create>(StringComparer.OrdinalIgnoreCase); + public static readonly FilePathToDocumentIdsMap Empty = new(s_emptyMap); + + private readonly ImmutableDictionary> _map; + + private FilePathToDocumentIdsMap(ImmutableDictionary> map) + { + _map = map; + } + + public bool TryGetValue(string filePath, out ImmutableArray documentIdsWithPath) + => _map.TryGetValue(filePath, out documentIdsWithPath); + + public static bool operator ==(FilePathToDocumentIdsMap left, FilePathToDocumentIdsMap right) + => left._map == right._map; + + public static bool operator !=(FilePathToDocumentIdsMap left, FilePathToDocumentIdsMap right) + => !(left == right); + + public override int GetHashCode() + => throw new NotSupportedException(); + + public override bool Equals([NotNullWhen(true)] object? obj) + => obj is FilePathToDocumentIdsMap map && Equals(map); + + public Builder ToBuilder() + => new(_map.ToBuilder()); + + public readonly struct Builder + { + private readonly ImmutableDictionary>.Builder _builder; + + public Builder(ImmutableDictionary>.Builder builder) + { + _builder = builder; + } + + public FilePathToDocumentIdsMap ToImmutable() + => new(_builder.ToImmutable()); + + public bool TryGetValue(string filePath, out ImmutableArray documentIdsWithPath) + => _builder.TryGetValue(filePath, out documentIdsWithPath); + + public void MultiAdd(string filePath, DocumentId documentId) + => _builder.MultiAdd(filePath, documentId); + + public void MultiRemove(string filePath, DocumentId documentId) + => _builder.MultiRemove(filePath, documentId); + } + } +} diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs index ff1ab471ec0a2..1503d236fed43 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs @@ -24,60 +24,6 @@ internal readonly record struct StateChange( ProjectState OldProjectState, ProjectState NewProjectState); - internal readonly struct FilePathToDocumentIdsMap - { - private static readonly ImmutableDictionary> s_emptyMap - = ImmutableDictionary.Create>(StringComparer.OrdinalIgnoreCase); - public static readonly FilePathToDocumentIdsMap Empty = new(s_emptyMap); - - private readonly ImmutableDictionary> _map; - - private FilePathToDocumentIdsMap(ImmutableDictionary> map) - { - _map = map; - } - - public bool TryGetValue(string filePath, out ImmutableArray documentIdsWithPath) - => _map.TryGetValue(filePath, out documentIdsWithPath); - - public static bool operator ==(FilePathToDocumentIdsMap left, FilePathToDocumentIdsMap right) - => left._map == right._map; - - public static bool operator !=(FilePathToDocumentIdsMap left, FilePathToDocumentIdsMap right) - => !(left == right); - - public override int GetHashCode() - => throw new NotSupportedException(); - - public override bool Equals([NotNullWhen(true)] object? obj) - => obj is FilePathToDocumentIdsMap map && Equals(map); - - public Builder ToBuilder() - => new(_map.ToBuilder()); - - public readonly struct Builder - { - private readonly ImmutableDictionary>.Builder _builder; - - public Builder(ImmutableDictionary>.Builder builder) - { - _builder = builder; - } - - public FilePathToDocumentIdsMap ToImmutable() - => new(_builder.ToImmutable()); - - public bool TryGetValue(string filePath, out ImmutableArray documentIdsWithPath) - => _builder.TryGetValue(filePath, out documentIdsWithPath); - - public void MultiAdd(string filePath, DocumentId documentId) - => _builder.MultiAdd(filePath, documentId); - - public void MultiRemove(string filePath, DocumentId documentId) - => _builder.MultiRemove(filePath, documentId); - } - } - /// /// Represents a set of projects and their source code documents. /// From df04375ea08dfb141000d35b4ef47ebede3fe33c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 26 Feb 2024 17:40:08 -0800 Subject: [PATCH 03/12] File scoped namespace --- .../Solution/FilePathToDocumentIdsMap.cs | 77 +++++++++---------- 1 file changed, 38 insertions(+), 39 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/FilePathToDocumentIdsMap.cs b/src/Workspaces/Core/Portable/Workspace/Solution/FilePathToDocumentIdsMap.cs index 58316915903df..dcda3d8c98635 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/FilePathToDocumentIdsMap.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/FilePathToDocumentIdsMap.cs @@ -7,59 +7,58 @@ using System.Diagnostics.CodeAnalysis; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis +namespace Microsoft.CodeAnalysis; + +internal readonly struct FilePathToDocumentIdsMap { - internal readonly struct FilePathToDocumentIdsMap - { - private static readonly ImmutableDictionary> s_emptyMap - = ImmutableDictionary.Create>(StringComparer.OrdinalIgnoreCase); - public static readonly FilePathToDocumentIdsMap Empty = new(s_emptyMap); + private static readonly ImmutableDictionary> s_emptyMap + = ImmutableDictionary.Create>(StringComparer.OrdinalIgnoreCase); + public static readonly FilePathToDocumentIdsMap Empty = new(s_emptyMap); - private readonly ImmutableDictionary> _map; + private readonly ImmutableDictionary> _map; - private FilePathToDocumentIdsMap(ImmutableDictionary> map) - { - _map = map; - } + private FilePathToDocumentIdsMap(ImmutableDictionary> map) + { + _map = map; + } - public bool TryGetValue(string filePath, out ImmutableArray documentIdsWithPath) - => _map.TryGetValue(filePath, out documentIdsWithPath); + public bool TryGetValue(string filePath, out ImmutableArray documentIdsWithPath) + => _map.TryGetValue(filePath, out documentIdsWithPath); - public static bool operator ==(FilePathToDocumentIdsMap left, FilePathToDocumentIdsMap right) - => left._map == right._map; + public static bool operator ==(FilePathToDocumentIdsMap left, FilePathToDocumentIdsMap right) + => left._map == right._map; - public static bool operator !=(FilePathToDocumentIdsMap left, FilePathToDocumentIdsMap right) - => !(left == right); + public static bool operator !=(FilePathToDocumentIdsMap left, FilePathToDocumentIdsMap right) + => !(left == right); - public override int GetHashCode() - => throw new NotSupportedException(); + public override int GetHashCode() + => throw new NotSupportedException(); - public override bool Equals([NotNullWhen(true)] object? obj) - => obj is FilePathToDocumentIdsMap map && Equals(map); + public override bool Equals([NotNullWhen(true)] object? obj) + => obj is FilePathToDocumentIdsMap map && Equals(map); - public Builder ToBuilder() - => new(_map.ToBuilder()); + public Builder ToBuilder() + => new(_map.ToBuilder()); - public readonly struct Builder - { - private readonly ImmutableDictionary>.Builder _builder; + public readonly struct Builder + { + private readonly ImmutableDictionary>.Builder _builder; - public Builder(ImmutableDictionary>.Builder builder) - { - _builder = builder; - } + public Builder(ImmutableDictionary>.Builder builder) + { + _builder = builder; + } - public FilePathToDocumentIdsMap ToImmutable() - => new(_builder.ToImmutable()); + public FilePathToDocumentIdsMap ToImmutable() + => new(_builder.ToImmutable()); - public bool TryGetValue(string filePath, out ImmutableArray documentIdsWithPath) - => _builder.TryGetValue(filePath, out documentIdsWithPath); + public bool TryGetValue(string filePath, out ImmutableArray documentIdsWithPath) + => _builder.TryGetValue(filePath, out documentIdsWithPath); - public void MultiAdd(string filePath, DocumentId documentId) - => _builder.MultiAdd(filePath, documentId); + public void MultiAdd(string filePath, DocumentId documentId) + => _builder.MultiAdd(filePath, documentId); - public void MultiRemove(string filePath, DocumentId documentId) - => _builder.MultiRemove(filePath, documentId); - } + public void MultiRemove(string filePath, DocumentId documentId) + => _builder.MultiRemove(filePath, documentId); } } From 789819746ffc651e6f4f7438e3bd5788efa66b38 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 26 Feb 2024 18:21:14 -0800 Subject: [PATCH 04/12] Allow frozen snapshots to store stale information about filepaths --- .../Solution/FilePathToDocumentIdsMap.cs | 34 ++++++++++++++----- .../Solution/SolutionCompilationState.cs | 31 ++++++++--------- .../Workspace/Solution/SolutionState.cs | 29 +++++++++++++--- 3 files changed, 65 insertions(+), 29 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/FilePathToDocumentIdsMap.cs b/src/Workspaces/Core/Portable/Workspace/Solution/FilePathToDocumentIdsMap.cs index dcda3d8c98635..3963746b1ccbc 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/FilePathToDocumentIdsMap.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/FilePathToDocumentIdsMap.cs @@ -9,24 +9,37 @@ namespace Microsoft.CodeAnalysis; +/// +/// Helper type that keeps track of all the file paths for all the documents in a solution snapshot and all the document +/// ids each maps to. +/// internal readonly struct FilePathToDocumentIdsMap { private static readonly ImmutableDictionary> s_emptyMap = ImmutableDictionary.Create>(StringComparer.OrdinalIgnoreCase); - public static readonly FilePathToDocumentIdsMap Empty = new(s_emptyMap); - + public static readonly FilePathToDocumentIdsMap Empty = new(isFrozen: false, s_emptyMap); + + /// + /// Whether or not this map corresponds to a frozen solution. Frozen solutions commonly drop many documents + /// (because only documents whose trees have been parsed are kept out). To keep things fast, instead of actually + /// dropping all those files from our we instead only keep track of added documents and mark that + /// we're frozen. Then, when a client actually asks for the document ids in a particular solution, we only return the + /// actual set present in that solution instance. + /// + public readonly bool IsFrozen; private readonly ImmutableDictionary> _map; - private FilePathToDocumentIdsMap(ImmutableDictionary> map) + private FilePathToDocumentIdsMap(bool isFrozen, ImmutableDictionary> map) { + IsFrozen = isFrozen; _map = map; } public bool TryGetValue(string filePath, out ImmutableArray documentIdsWithPath) - => _map.TryGetValue(filePath, out documentIdsWithPath); + => _map.TryGetValue(filePath, out documentIdsWithPath); public static bool operator ==(FilePathToDocumentIdsMap left, FilePathToDocumentIdsMap right) - => left._map == right._map; + => left.IsFrozen == right.IsFrozen && left._map == right._map; public static bool operator !=(FilePathToDocumentIdsMap left, FilePathToDocumentIdsMap right) => !(left == right); @@ -38,19 +51,24 @@ public override bool Equals([NotNullWhen(true)] object? obj) => obj is FilePathToDocumentIdsMap map && Equals(map); public Builder ToBuilder() - => new(_map.ToBuilder()); + => new(IsFrozen, _map.ToBuilder()); + + public FilePathToDocumentIdsMap ToFrozen() + => IsFrozen ? this : new(isFrozen: true, _map); public readonly struct Builder { + private readonly bool _isFrozen; private readonly ImmutableDictionary>.Builder _builder; - public Builder(ImmutableDictionary>.Builder builder) + public Builder(bool isFrozen, ImmutableDictionary>.Builder builder) { + _isFrozen = isFrozen; _builder = builder; } public FilePathToDocumentIdsMap ToImmutable() - => new(_builder.ToImmutable()); + => new(_isFrozen, _builder.ToImmutable()); public bool TryGetValue(string filePath, out ImmutableArray documentIdsWithPath) => _builder.TryGetValue(filePath, out documentIdsWithPath); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs index b25a591d8c853..ade535c9a3d98 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs @@ -1064,8 +1064,18 @@ private SolutionCompilationState ComputeFrozenSnapshot(CancellationToken cancell var newIdToProjectStateMapBuilder = this.SolutionState.ProjectStates.ToBuilder(); var newIdToTrackerMapBuilder = _projectIdToTrackerMap.ToBuilder(); - var filePathToDocumentIdsMapBuilder = this.SolutionState.FilePathToDocumentIdsMap.ToBuilder(); - var filePathToDocumentIdsMapChanged = false; + // Keep track of the files that were potentially added between the last frozen snapshot point we have for a + // project and now. Specifically, if a file was removed in reality, it may show us as an add as we are + // effectively jumping back to a prior point in time for a particular project. We want all those files (and + // related doc ids) to be present in the frozen solution we hand back. + // + // Note: we only keep track of added files. We do not keep track of removed files. This is intentionally done + // for performance reasons. Specifically, it is quite normal for a project to drop all documents when frozen + // (for example, when no documents have been parsed in it). Actually dropping all these files from this map is + // very expensive. This does mean that the FilePathToDocumentIdsMap will be a superset of all files. That's + // ok. We'll mark this map as being frozen (and thus potentially containing a superset of legal ids), and later + // on our helpers will check for that and filter down to the set that is in a solution when queried. + var filePathToDocumentIdsMapBuilder = this.SolutionState.FilePathToDocumentIdsMap.ToFrozen().ToBuilder(); foreach (var projectId in this.SolutionState.ProjectIds) { @@ -1113,9 +1123,7 @@ private SolutionCompilationState ComputeFrozenSnapshot(CancellationToken cancell var newIdToProjectStateMap = newIdToProjectStateMapBuilder.ToImmutable(); var newIdToTrackerMap = newIdToTrackerMapBuilder.ToImmutable(); - FilePathToDocumentIdsMap? filePathToDocumentIdsMap = filePathToDocumentIdsMapChanged - ? filePathToDocumentIdsMapBuilder.ToImmutable() - : null; + var filePathToDocumentIdsMap = filePathToDocumentIdsMapBuilder.ToImmutable(); var dependencyGraph = SolutionState.CreateDependencyGraph(this.SolutionState.ProjectIds, newIdToProjectStateMap); @@ -1139,19 +1147,9 @@ void CheckDocumentStates( if (oldStates.Equals(newStates)) return; - // Get the trivial sets of documents that are present in one set but not the other. - + // Keep track of files that are definitely added. Make sure the added doc is in the file path map. foreach (var documentId in newStates.GetAddedStateIds(oldStates)) - { - filePathToDocumentIdsMapChanged = true; SolutionState.AddDocumentFilePath(newStates.GetRequiredState(documentId), filePathToDocumentIdsMapBuilder); - } - - foreach (var documentId in newStates.GetRemovedStateIds(oldStates)) - { - filePathToDocumentIdsMapChanged = true; - SolutionState.RemoveDocumentFilePath(oldStates.GetRequiredState(documentId), filePathToDocumentIdsMapBuilder); - } // Now go through the states that are in both sets. We have to check these all as it is possible for // document to change its file path without its id changing. @@ -1161,7 +1159,6 @@ void CheckDocumentStates( oldDocumentState != newDocumentState && oldDocumentState.FilePath != newDocumentState.FilePath) { - filePathToDocumentIdsMapChanged = true; SolutionState.RemoveDocumentFilePath(oldDocumentState, filePathToDocumentIdsMapBuilder); SolutionState.AddDocumentFilePath(newDocumentState, filePathToDocumentIdsMapBuilder); } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs index 1503d236fed43..4a82d9c0aff98 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -1209,13 +1210,33 @@ public StateChange ForkProject( public ImmutableArray GetDocumentIdsWithFilePath(string? filePath) { if (string.IsNullOrEmpty(filePath)) - { return []; + + if (!_filePathToDocumentIdsMap.TryGetValue(filePath, out var documentIds)) + return []; + + // If this wasn't the result of a freeze, then we can return the document ids as is. They should be accurate. + if (!_filePathToDocumentIdsMap.IsFrozen) + { + Debug.Assert(documentIds.All(ContainsAnyDocument)); + return documentIds; } - return _filePathToDocumentIdsMap.TryGetValue(filePath, out var documentIds) - ? documentIds - : []; + // We were frozen. So we may be seeing document ids that no longer exist within this snapshot. If so, + // filter them out. + using var _ = ArrayBuilder.GetInstance(documentIds.Length, out var result); + + foreach (var documentId in documentIds) + { + if (ContainsAnyDocument(documentId)) + result.Add(documentId); + } + + result.RemoveDuplicates(); + return result.ToImmutableAndClear(); + + bool ContainsAnyDocument(DocumentId documentId) + => this.ContainsDocument(documentId) || this.ContainsAdditionalDocument(documentId) || this.ContainsAnalyzerConfigDocument(documentId); } public static ProjectDependencyGraph CreateDependencyGraph( From 51114a75e18e7c6d05b9e597e9d218c84eff8f6b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 26 Feb 2024 19:54:18 -0800 Subject: [PATCH 05/12] Pass explicitly --- .../Workspace/Solution/SolutionCompilationState.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs index ade535c9a3d98..d0e54c86fae71 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs @@ -1114,9 +1114,9 @@ private SolutionCompilationState ComputeFrozenSnapshot(CancellationToken cancell if (oldProjectState != newProjectState) { - CheckDocumentStates(oldProjectState.DocumentStates, newProjectState.DocumentStates); - CheckDocumentStates(oldProjectState.AdditionalDocumentStates, newProjectState.AdditionalDocumentStates); - CheckDocumentStates(oldProjectState.AnalyzerConfigDocumentStates, newProjectState.AnalyzerConfigDocumentStates); + CheckDocumentStates(filePathToDocumentIdsMapBuilder, oldProjectState.DocumentStates, newProjectState.DocumentStates); + CheckDocumentStates(filePathToDocumentIdsMapBuilder, oldProjectState.AdditionalDocumentStates, newProjectState.AdditionalDocumentStates); + CheckDocumentStates(filePathToDocumentIdsMapBuilder, oldProjectState.AnalyzerConfigDocumentStates, newProjectState.AnalyzerConfigDocumentStates); } } @@ -1140,7 +1140,8 @@ private SolutionCompilationState ComputeFrozenSnapshot(CancellationToken cancell return newCompilationState; - void CheckDocumentStates( + static void CheckDocumentStates( + FilePathToDocumentIdsMap.Builder filePathToDocumentIdsMapBuilder, TextDocumentStates oldStates, TextDocumentStates newStates) where TDocumentState : TextDocumentState { From aefef8c8c87d29933103ef756bd932c88891be60 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 26 Feb 2024 19:59:44 -0800 Subject: [PATCH 06/12] Simplify --- .../Solution/FilePathToDocumentIdsMap.cs | 21 ++++++++++++--- .../Solution/SolutionCompilationState.cs | 4 +-- .../Workspace/Solution/SolutionState.cs | 26 ++----------------- 3 files changed, 21 insertions(+), 30 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/FilePathToDocumentIdsMap.cs b/src/Workspaces/Core/Portable/Workspace/Solution/FilePathToDocumentIdsMap.cs index 3963746b1ccbc..4b7e60d456bf1 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/FilePathToDocumentIdsMap.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/FilePathToDocumentIdsMap.cs @@ -73,10 +73,23 @@ public FilePathToDocumentIdsMap ToImmutable() public bool TryGetValue(string filePath, out ImmutableArray documentIdsWithPath) => _builder.TryGetValue(filePath, out documentIdsWithPath); - public void MultiAdd(string filePath, DocumentId documentId) - => _builder.MultiAdd(filePath, documentId); + public void MultiAdd(string? filePath, DocumentId documentId) + { + if (RoslynString.IsNullOrEmpty(filePath)) + return; + + _builder.MultiAdd(filePath, documentId); + } - public void MultiRemove(string filePath, DocumentId documentId) - => _builder.MultiRemove(filePath, documentId); + public void MultiRemove(string? filePath, DocumentId documentId) + { + if (RoslynString.IsNullOrEmpty(filePath)) + return; + + if (!this.TryGetValue(filePath, out var documentIdsWithPath) || !documentIdsWithPath.Contains(documentId)) + throw new ArgumentException($"The given documentId was not found"); + + _builder.MultiRemove(filePath, documentId); + } } } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs index d0e54c86fae71..2af6e1e09d4e7 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs @@ -1160,8 +1160,8 @@ static void CheckDocumentStates( oldDocumentState != newDocumentState && oldDocumentState.FilePath != newDocumentState.FilePath) { - SolutionState.RemoveDocumentFilePath(oldDocumentState, filePathToDocumentIdsMapBuilder); - SolutionState.AddDocumentFilePath(newDocumentState, filePathToDocumentIdsMapBuilder); + filePathToDocumentIdsMapBuilder.MultiRemove(oldDocumentState.FilePath, oldDocumentState.Id); + filePathToDocumentIdsMapBuilder.MultiAdd(newDocumentState.FilePath, newDocumentState.Id); } } } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs index 4a82d9c0aff98..35aaf014c0a21 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs @@ -417,17 +417,7 @@ public FilePathToDocumentIdsMap CreateFilePathToDocumentIdsMapWithAddedDocuments private static void AddDocumentFilePaths(IEnumerable documentStates, FilePathToDocumentIdsMap.Builder builder) { foreach (var documentState in documentStates) - AddDocumentFilePath(documentState, builder); - } - - public static void AddDocumentFilePath(TextDocumentState documentState, FilePathToDocumentIdsMap.Builder builder) - { - var filePath = documentState.FilePath; - - if (RoslynString.IsNullOrEmpty(filePath)) - return; - - builder.MultiAdd(filePath, documentState.Id); + builder.MultiAdd(documentState.FilePath, documentState.Id); } public FilePathToDocumentIdsMap CreateFilePathToDocumentIdsMapWithRemovedDocuments(IEnumerable documentStates) @@ -440,19 +430,7 @@ public FilePathToDocumentIdsMap CreateFilePathToDocumentIdsMapWithRemovedDocumen private static void RemoveDocumentFilePaths(IEnumerable documentStates, FilePathToDocumentIdsMap.Builder builder) { foreach (var documentState in documentStates) - RemoveDocumentFilePath(documentState, builder); - } - - public static void RemoveDocumentFilePath(TextDocumentState documentState, FilePathToDocumentIdsMap.Builder builder) - { - var filePath = documentState.FilePath; - if (RoslynString.IsNullOrEmpty(filePath)) - return; - - if (!builder.TryGetValue(filePath, out var documentIdsWithPath) || !documentIdsWithPath.Contains(documentState.Id)) - throw new ArgumentException($"The given documentId was not found in '{nameof(_filePathToDocumentIdsMap)}'."); - - builder.MultiRemove(filePath, documentState.Id); + builder.MultiRemove(documentState.FilePath, documentState.Id); } private FilePathToDocumentIdsMap CreateFilePathToDocumentIdsMapWithFilePath(DocumentId documentId, string? oldFilePath, string? newFilePath) From 9d3058a9fabf1c2349db8b51a2b989e76eb9ac6f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 26 Feb 2024 20:05:08 -0800 Subject: [PATCH 07/12] Inline method --- .../Solution/FilePathToDocumentIdsMap.cs | 4 ++-- .../Solution/SolutionCompilationState.cs | 6 +++--- .../Portable/Workspace/Solution/SolutionState.cs | 15 ++++----------- 3 files changed, 9 insertions(+), 16 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/FilePathToDocumentIdsMap.cs b/src/Workspaces/Core/Portable/Workspace/Solution/FilePathToDocumentIdsMap.cs index 4b7e60d456bf1..8bee3099b713a 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/FilePathToDocumentIdsMap.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/FilePathToDocumentIdsMap.cs @@ -73,7 +73,7 @@ public FilePathToDocumentIdsMap ToImmutable() public bool TryGetValue(string filePath, out ImmutableArray documentIdsWithPath) => _builder.TryGetValue(filePath, out documentIdsWithPath); - public void MultiAdd(string? filePath, DocumentId documentId) + public void Add(string? filePath, DocumentId documentId) { if (RoslynString.IsNullOrEmpty(filePath)) return; @@ -81,7 +81,7 @@ public void MultiAdd(string? filePath, DocumentId documentId) _builder.MultiAdd(filePath, documentId); } - public void MultiRemove(string? filePath, DocumentId documentId) + public void Remove(string? filePath, DocumentId documentId) { if (RoslynString.IsNullOrEmpty(filePath)) return; diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs index 2af6e1e09d4e7..b9073ea095c0f 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs @@ -1150,7 +1150,7 @@ static void CheckDocumentStates( // Keep track of files that are definitely added. Make sure the added doc is in the file path map. foreach (var documentId in newStates.GetAddedStateIds(oldStates)) - SolutionState.AddDocumentFilePath(newStates.GetRequiredState(documentId), filePathToDocumentIdsMapBuilder); + filePathToDocumentIdsMapBuilder.Add(newStates.GetRequiredState(documentId).FilePath, documentId); // Now go through the states that are in both sets. We have to check these all as it is possible for // document to change its file path without its id changing. @@ -1160,8 +1160,8 @@ static void CheckDocumentStates( oldDocumentState != newDocumentState && oldDocumentState.FilePath != newDocumentState.FilePath) { - filePathToDocumentIdsMapBuilder.MultiRemove(oldDocumentState.FilePath, oldDocumentState.Id); - filePathToDocumentIdsMapBuilder.MultiAdd(newDocumentState.FilePath, newDocumentState.Id); + filePathToDocumentIdsMapBuilder.Remove(oldDocumentState.FilePath, oldDocumentState.Id); + filePathToDocumentIdsMapBuilder.Add(newDocumentState.FilePath, newDocumentState.Id); } } } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs index 35aaf014c0a21..02e5108408ea4 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs @@ -417,7 +417,7 @@ public FilePathToDocumentIdsMap CreateFilePathToDocumentIdsMapWithAddedDocuments private static void AddDocumentFilePaths(IEnumerable documentStates, FilePathToDocumentIdsMap.Builder builder) { foreach (var documentState in documentStates) - builder.MultiAdd(documentState.FilePath, documentState.Id); + builder.Add(documentState.FilePath, documentState.Id); } public FilePathToDocumentIdsMap CreateFilePathToDocumentIdsMapWithRemovedDocuments(IEnumerable documentStates) @@ -430,7 +430,7 @@ public FilePathToDocumentIdsMap CreateFilePathToDocumentIdsMapWithRemovedDocumen private static void RemoveDocumentFilePaths(IEnumerable documentStates, FilePathToDocumentIdsMap.Builder builder) { foreach (var documentState in documentStates) - builder.MultiRemove(documentState.FilePath, documentState.Id); + builder.Remove(documentState.FilePath, documentState.Id); } private FilePathToDocumentIdsMap CreateFilePathToDocumentIdsMapWithFilePath(DocumentId documentId, string? oldFilePath, string? newFilePath) @@ -442,15 +442,8 @@ private FilePathToDocumentIdsMap CreateFilePathToDocumentIdsMapWithFilePath(Docu var builder = _filePathToDocumentIdsMap.ToBuilder(); - if (!RoslynString.IsNullOrEmpty(oldFilePath)) - { - builder.MultiRemove(oldFilePath, documentId); - } - - if (!RoslynString.IsNullOrEmpty(newFilePath)) - { - builder.MultiAdd(newFilePath, documentId); - } + builder.Remove(oldFilePath, documentId); + builder.Add(newFilePath, documentId); return builder.ToImmutable(); } From b4d1cc494d6a1e8080f57b82d53cc4f7b62b2fc6 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 26 Feb 2024 20:07:17 -0800 Subject: [PATCH 08/12] Share work --- .../Core/Portable/Workspace/Solution/SolutionState.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs index 02e5108408ea4..f5e689210026a 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs @@ -1207,7 +1207,15 @@ public ImmutableArray GetDocumentIdsWithFilePath(string? filePath) return result.ToImmutableAndClear(); bool ContainsAnyDocument(DocumentId documentId) - => this.ContainsDocument(documentId) || this.ContainsAdditionalDocument(documentId) || this.ContainsAnalyzerConfigDocument(documentId); + { + var project = this.GetProjectState(documentId.ProjectId); + if (project is null) + return false; + + return project.DocumentStates.Contains(documentId) + || project.AdditionalDocumentStates.Contains(documentId) + || project.AnalyzerConfigDocumentStates.Contains(documentId); + } } public static ProjectDependencyGraph CreateDependencyGraph( From 40b57e0cee4cfad0bf423e269fa37b6d24cd4536 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 26 Feb 2024 22:27:26 -0800 Subject: [PATCH 09/12] Only remove if we had a match --- .../Compiler/Core/Utilities/IDictionaryExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/IDictionaryExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/IDictionaryExtensions.cs index 4c0fc960372a9..58b1ed03e457e 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/IDictionaryExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/IDictionaryExtensions.cs @@ -214,7 +214,7 @@ public static void MultiRemove(this IDictionary.Default.Equals(collection[0], value)) { dictionary.Remove(key); } From 33bbdd6d6a443a8af6ad470b149c653035903f85 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 27 Feb 2024 09:06:24 -0800 Subject: [PATCH 10/12] Rename --- .../Workspace/Solution/SolutionCompilationState.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs index b9073ea095c0f..0671c31b26ee2 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs @@ -1114,9 +1114,9 @@ private SolutionCompilationState ComputeFrozenSnapshot(CancellationToken cancell if (oldProjectState != newProjectState) { - CheckDocumentStates(filePathToDocumentIdsMapBuilder, oldProjectState.DocumentStates, newProjectState.DocumentStates); - CheckDocumentStates(filePathToDocumentIdsMapBuilder, oldProjectState.AdditionalDocumentStates, newProjectState.AdditionalDocumentStates); - CheckDocumentStates(filePathToDocumentIdsMapBuilder, oldProjectState.AnalyzerConfigDocumentStates, newProjectState.AnalyzerConfigDocumentStates); + AddMissingOrChangedFilePathMappings(filePathToDocumentIdsMapBuilder, oldProjectState.DocumentStates, newProjectState.DocumentStates); + AddMissingOrChangedFilePathMappings(filePathToDocumentIdsMapBuilder, oldProjectState.AdditionalDocumentStates, newProjectState.AdditionalDocumentStates); + AddMissingOrChangedFilePathMappings(filePathToDocumentIdsMapBuilder, oldProjectState.AnalyzerConfigDocumentStates, newProjectState.AnalyzerConfigDocumentStates); } } @@ -1140,7 +1140,7 @@ private SolutionCompilationState ComputeFrozenSnapshot(CancellationToken cancell return newCompilationState; - static void CheckDocumentStates( + static void AddMissingOrChangedFilePathMappings( FilePathToDocumentIdsMap.Builder filePathToDocumentIdsMapBuilder, TextDocumentStates oldStates, TextDocumentStates newStates) where TDocumentState : TextDocumentState From aeefd644c06c0fca7f98917261916d3d023201a7 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 27 Feb 2024 09:10:01 -0800 Subject: [PATCH 11/12] Simplify --- .../Solution/SolutionCompilationState.cs | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs index 0671c31b26ee2..b5001a72c996d 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs @@ -1148,18 +1148,20 @@ static void AddMissingOrChangedFilePathMappings( if (oldStates.Equals(newStates)) return; - // Keep track of files that are definitely added. Make sure the added doc is in the file path map. - foreach (var documentId in newStates.GetAddedStateIds(oldStates)) - filePathToDocumentIdsMapBuilder.Add(newStates.GetRequiredState(documentId).FilePath, documentId); - - // Now go through the states that are in both sets. We have to check these all as it is possible for - // document to change its file path without its id changing. - foreach (var (documentId, oldDocumentState) in oldStates.States) + foreach (var (documentId, newDocumentState) in newStates.States) { - if (newStates.States.TryGetValue(documentId, out var newDocumentState) && - oldDocumentState != newDocumentState && - oldDocumentState.FilePath != newDocumentState.FilePath) + if (!oldStates.TryGetState(documentId, out var oldDocumentState)) + { + // Keep track of files that are definitely added. Make sure the added doc is in the file path map. + filePathToDocumentIdsMapBuilder.Add(newDocumentState.FilePath, documentId); + + } + else if (oldDocumentState != newDocumentState && + oldDocumentState.FilePath != newDocumentState.FilePath) { + // Otherwise, if the document is in both, but the file name changed, then remove the old mapping + // and add the new mapping. Importantly, we don't want other linked files with the *old* path + // to consider this document one of their linked brethren. filePathToDocumentIdsMapBuilder.Remove(oldDocumentState.FilePath, oldDocumentState.Id); filePathToDocumentIdsMapBuilder.Add(newDocumentState.FilePath, newDocumentState.Id); } From 0d1ab07a977661acbcf32e227374b6f36d592c06 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 27 Feb 2024 09:16:45 -0800 Subject: [PATCH 12/12] Add docs --- .../Workspace/Solution/SolutionCompilationState.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs index b5001a72c996d..5921cf223bba4 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs @@ -1148,6 +1148,13 @@ static void AddMissingOrChangedFilePathMappings( if (oldStates.Equals(newStates)) return; + // We want to make sure that all the documents in the new-state are properly represented in the file map. + // It's ok if old-state documents are still in the map as GetDocumentIdsWithFilePath will filter them out + // later since we're producing a frozen-partial map. + // + // Iterating over the new-states has an additional benefit. For projects that haven't ever been looked at + // (so they haven't really parsed any documents), this will results in empty new-states. So this loop will + // be almost a no-op for most non-relevant projects. foreach (var (documentId, newDocumentState) in newStates.States) { if (!oldStates.TryGetState(documentId, out var oldDocumentState))