diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs index 80daf417c9f21..5352bcfa18e35 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs @@ -765,6 +765,9 @@ public AnalyzerReference GetAnalyzerReferenceForGenerator(ISourceGenerator gener public ProjectState AddDocuments(ImmutableArray documents) { + if (documents.IsEmpty) + return this; + Debug.Assert(!documents.Any(d => DocumentStates.Contains(d.Id))); return With( @@ -774,6 +777,9 @@ public ProjectState AddDocuments(ImmutableArray documents) public ProjectState AddAdditionalDocuments(ImmutableArray documents) { + if (documents.IsEmpty) + return this; + Debug.Assert(!documents.Any(d => AdditionalDocumentStates.Contains(d.Id))); return With( @@ -783,6 +789,9 @@ public ProjectState AddAdditionalDocuments(ImmutableArray documents) { + if (documents.IsEmpty) + return this; + Debug.Assert(!documents.Any(d => AnalyzerConfigDocumentStates.Contains(d.Id))); var newAnalyzerConfigDocumentStates = AnalyzerConfigDocumentStates.AddRange(documents); @@ -811,6 +820,9 @@ private ProjectState CreateNewStateForChangedAnalyzerConfigDocuments(TextDocumen public ProjectState RemoveDocuments(ImmutableArray documentIds) { + if (documentIds.IsEmpty) + return this; + // We create a new CachingAnalyzerConfigSet for the new snapshot to avoid holding onto cached information // for removed documents. return With( @@ -821,6 +833,9 @@ public ProjectState RemoveDocuments(ImmutableArray documentIds) public ProjectState RemoveAdditionalDocuments(ImmutableArray documentIds) { + if (documentIds.IsEmpty) + return this; + return With( projectInfo: ProjectInfo.WithVersion(Version.GetNewerVersion()), additionalDocumentStates: AdditionalDocumentStates.RemoveRange(documentIds)); @@ -828,13 +843,19 @@ public ProjectState RemoveAdditionalDocuments(ImmutableArray documen public ProjectState RemoveAnalyzerConfigDocuments(ImmutableArray documentIds) { + if (documentIds.IsEmpty) + return this; + var newAnalyzerConfigDocumentStates = AnalyzerConfigDocumentStates.RemoveRange(documentIds); return CreateNewStateForChangedAnalyzerConfigDocuments(newAnalyzerConfigDocumentStates); } - public ProjectState RemoveAllDocuments() + public ProjectState RemoveAllNormalDocuments() { + if (DocumentStates.IsEmpty) + return this; + // We create a new CachingAnalyzerConfigSet for the new snapshot to avoid holding onto cached information // for removed documents. return With( diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs index 60f6fed82f1ec..5d26938edc036 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs @@ -311,7 +311,7 @@ InProgressState BuildInProgressStateFromNoCompilationState() // other. It also means that if we're in the process of parsing documents in that chain, that // we'll see the results of how far we've gotten if someone asks for a frozen snapshot midway // through. - var initialProjectState = this.ProjectState.RemoveAllDocuments(); + var initialProjectState = this.ProjectState.RemoveAllNormalDocuments(); var initialCompilation = this.CreateEmptyCompilation(); var translationActionsBuilder = ImmutableList.CreateBuilder(); @@ -723,7 +723,7 @@ public ICompilationTracker FreezePartialState(CancellationToken cancellationToke // Transition us to a state that only has documents for the files we've already parsed. var frozenProjectState = this.ProjectState - .RemoveAllDocuments() + .RemoveAllNormalDocuments() .AddDocuments(documentsWithTreesBuilder.ToImmutableAndClear()); // Defer creating these compilations. It's common to freeze projects (as part of a solution freeze) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentStates.cs b/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentStates.cs index 1f34f1af7c22c..781027bee1db2 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentStates.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentStates.cs @@ -11,6 +11,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Serialization; using Roslyn.Utilities; @@ -141,6 +142,21 @@ public TextDocumentStates AddRange(ImmutableArray states) public TextDocumentStates RemoveRange(ImmutableArray ids) { + if (ids.Length == _ids.Count) + { + using var _ = PooledHashSet.GetInstance(out var set); + +#if NETCOREAPP + set.EnsureCapacity(ids.Length); +#endif + + foreach (var documentId in _ids) + set.Add(documentId); + + if (ids.All(static (id, set) => set.Contains(id), set)) + return Empty; + } + IEnumerable enumerableIds = ids; return new(_ids.RemoveRange(enumerableIds), _map.RemoveRange(enumerableIds)); }