From 92e8fdbcf89f88cd9e632cf68d482a79c392de7c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 13 Feb 2024 12:22:56 -0800 Subject: [PATCH 01/13] Defer parsing syntax trees when computing frozen partial solutions --- ...pilationTracker.CompilationTrackerState.cs | 4 +- ...tionCompilationState.CompilationTracker.cs | 119 +++++++----------- ...eneratedFileReplacingCompilationTracker.cs | 2 +- ...ionCompilationState.ICompilationTracker.cs | 2 +- .../Solution/SolutionCompilationState.cs | 12 +- .../CoreTest/SolutionTests/SolutionTests.cs | 8 +- 6 files changed, 62 insertions(+), 85 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.CompilationTrackerState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.CompilationTrackerState.cs index bc455b1baf4c2..f391116d7b16e 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.CompilationTrackerState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.CompilationTrackerState.cs @@ -91,7 +91,7 @@ private InProgressState( Compilation compilationWithoutGeneratedDocuments, CompilationTrackerGeneratorInfo generatorInfo, Compilation? staleCompilationWithGeneratedDocuments, - ImmutableList<(ProjectState state, CompilationAndGeneratorDriverTranslationAction action)> pendingTranslationSteps) + ImmutableList<(ProjectState oldState, CompilationAndGeneratorDriverTranslationAction action)> pendingTranslationSteps) : base(isFrozen, compilationWithoutGeneratedDocuments, generatorInfo) @@ -108,7 +108,7 @@ public static InProgressState Create( Compilation compilationWithoutGeneratedDocuments, CompilationTrackerGeneratorInfo generatorInfo, Compilation? staleCompilationWithGeneratedDocuments, - ImmutableList<(ProjectState state, CompilationAndGeneratorDriverTranslationAction action)> pendingTranslationSteps) + ImmutableList<(ProjectState oldState, CompilationAndGeneratorDriverTranslationAction action)> pendingTranslationSteps) { Contract.ThrowIfTrue(pendingTranslationSteps is null); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs index 9289528d9afda..76015ed0c878d 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs @@ -187,95 +187,86 @@ public ICompilationTracker Fork( } } - public ICompilationTracker FreezePartialStateWithTree( + public ICompilationTracker FreezePartialStateWithDocument( SolutionCompilationState compilationState, DocumentState docState, - SyntaxTree tree, CancellationToken cancellationToken) { GetPartialCompilationState( - compilationState, docState.Id, + compilationState, + docState.Id, out var inProgressProject, out var compilationPair, out var generatorInfo, - out var metadataReferenceToProjectId, cancellationToken); - // Ensure we actually have the tree we need in there; note that if the tree is present, then we know the document must also be - // present in inProgressProject, since those are both updated in parallel. - // - // the tree that we have been given was directly returned from the document state that we're also being passed -- - // the only reason we're requesting it earlier is this code is running under a lock in - // SolutionState.WithFrozenPartialCompilationIncludingSpecificDocument. - if (!compilationPair.CompilationWithoutGeneratedDocuments.ContainsSyntaxTree(tree)) + var builder = ImmutableList.CreateBuilder<(ProjectState, CompilationAndGeneratorDriverTranslationAction)>(); + + if (inProgressProject.DocumentStates.TryGetState(docState.Id, out var oldState) && oldState == docState) { - // We do not have the exact tree. It either means this document was recently added, or the tree was recently changed. - // We now need to update both the inProgressState and the compilation. There are several possibilities we want to consider: + // Existing state matches the state we're trying to move to. No work to do. Just freeze us at our + // current position. + } + else + { + // We do not have the exact document. It either means this document was recently added, or the + // document was recently changed. We now need to update both the inProgressState and the + // compilation. There are several possibilities we want to consider: // // 1. An earlier version of the document is present in the compilation, and we just need to update it to the current version - // 2. The tree wasn't present in the original snapshot at all, and we just need to add the tree. - // 3. The tree wasn't present in the original snapshot, but an older file had been removed that had the same file path. - // As a heuristic, we remove the old one so we don't end up with duplicate trees. + // 2. The document wasn't present in the original snapshot at all, and we just need to add the + // document. + // 3. The document wasn't present in the original snapshot, but an older file had been removed that + // had the same file path. As a heuristic, we remove the old one so we don't end up with + // duplicate documents. // - // Note it's possible that we simply had never tried to produce a compilation yet for this project at all, in that case - // GetPartialCompilationState would have produced an empty compilation, and it would have updated inProgressProject to - // remove all the documents. Thus, that is no different than the "add" case above. - if (inProgressProject.DocumentStates.TryGetState(docState.Id, out var oldState)) + // Note it's possible that we simply had never tried to produce a compilation yet for this project + // at all, in that case GetPartialCompilationState would have produced an empty compilation, and it + // would have updated inProgressProject to remove all the documents. Thus, that is no different than + // the "add" case above. + if (oldState != null) { - // Scenario 1. The document had been previously parsed and it's there, so we can update it with our current state - // This call should be instant, since the compilation already must exist that contains this tree. Note if no compilation existed - // GetPartialCompilationState would have produced an empty one, and removed any documents, so inProgressProject.DocumentStates would + // Scenario 1. The document had been previously parsed and it's there, so we can update it with + // our current state. Note if no compilation existed GetPartialCompilationState would have + // produced an empty one, and removed any documents, so inProgressProject.DocumentStates would // have been empty originally. - var oldTree = oldState.GetSyntaxTree(cancellationToken); - - compilationPair = compilationPair.ReplaceSyntaxTree(oldTree, tree); + builder.Add((inProgressProject, new CompilationAndGeneratorDriverTranslationAction.TouchDocumentAction(oldState, docState))); inProgressProject = inProgressProject.UpdateDocument(docState, contentChanged: true); } else { - // We're in either scenario 2 or 3. Do we have an existing tree to try replacing? Note: the file path here corresponds to Document.FilePath. - // If a document's file path is null, we then substitute Document.Name, so we usually expect there to be a unique string regardless. - var oldTree = compilationPair.CompilationWithoutGeneratedDocuments.SyntaxTrees.FirstOrDefault(t => t.FilePath == tree.FilePath); - if (oldTree == null) + // We're in either scenario 2 or 3. Do we have an existing document to try replacing? Note: the + // file path here corresponds to Document.FilePath. If a document's file path is null, we then + // substitute Document.Name, so we usually expect there to be a unique string regardless. + oldState = inProgressProject.DocumentStates.States.FirstOrDefault(kvp => kvp.Value.FilePath == docState.FilePath).Value; + if (oldState is null) { // Scenario 2. - compilationPair = compilationPair.AddSyntaxTree(tree); + builder.Add((inProgressProject, new CompilationAndGeneratorDriverTranslationAction.AddDocumentsAction([docState]))); inProgressProject = inProgressProject.AddDocuments([docState]); } else { - // Scenario 3. - compilationPair = compilationPair.ReplaceSyntaxTree(oldTree, tree); - - // The old tree came from some other document with a different ID then we started with -- if the document ID still existed we would have - // been in the Scenario 1 case instead. We'll find the old document ID, remove that state, and then add ours. - var oldDocumentId = DocumentState.GetDocumentIdForTree(oldTree); - Contract.ThrowIfNull(oldDocumentId, $"{nameof(oldTree)} came from the compilation produced by the workspace, so the document ID should have existed."); + // Scenario 3. The old doc came from some other document with a different ID then we started + // with -- if the document ID still existed we would have been in the Scenario 1 case + // instead. We'll find the old document ID, remove that state, and then add ours. + builder.Add((inProgressProject, new CompilationAndGeneratorDriverTranslationAction.RemoveDocumentsAction([oldState]))); + builder.Add((inProgressProject, new CompilationAndGeneratorDriverTranslationAction.AddDocumentsAction([docState]))); inProgressProject = inProgressProject - .RemoveDocuments([oldDocumentId]) + .RemoveDocuments([oldState.Id]) .AddDocuments([docState]); } } } - // At this point, we now absolutely should have our tree in the compilation - Contract.ThrowIfFalse(compilationPair.CompilationWithoutGeneratedDocuments.ContainsSyntaxTree(tree)); - - // Mark our FinalState as frozen. We'll want to keep whatever we have here through whatever future - // transformations occur. - - var finalState = FinalCompilationTrackerState.Create( + var inProgressState = InProgressState.Create( isFrozen: true, - finalCompilationWithGeneratedDocuments: compilationPair.CompilationWithGeneratedDocuments, - compilationWithoutGeneratedDocuments: compilationPair.CompilationWithoutGeneratedDocuments, - // As a policy, all partial-state projects are said to have incomplete references, since the state - // has no guarantees. - hasSuccessfullyLoaded: false, + compilationPair.CompilationWithoutGeneratedDocuments, generatorInfo, - this.ProjectState.Id, - metadataReferenceToProjectId); + compilationPair.CompilationWithGeneratedDocuments, + builder.ToImmutable()); - return new CompilationTracker(inProgressProject, finalState, this.SkeletonReferenceCache.Clone()); + return new CompilationTracker(inProgressProject, inProgressState, this.SkeletonReferenceCache.Clone()); } /// @@ -291,7 +282,6 @@ private void GetPartialCompilationState( out ProjectState inProgressProject, out CompilationPair compilations, out CompilationTrackerGeneratorInfo generatorInfo, - out Dictionary? metadataReferenceToProjectId, CancellationToken cancellationToken) { var state = ReadState(); @@ -319,9 +309,6 @@ private void GetPartialCompilationState( compilationWithoutGeneratedDocuments, compilationWithoutGeneratedDocuments.AddSyntaxTrees(generatorInfo.Documents.States.Values.Select(state => state.GetSyntaxTree(cancellationToken)))); - // This is likely a bug. It seems possible to pass out a partial compilation state that we don't - // properly record assembly symbols for. - metadataReferenceToProjectId = null; return; } @@ -332,12 +319,6 @@ private void GetPartialCompilationState( Contract.ThrowIfNull(finalCompilation, "We have a FinalState, so we must have a non-null final compilation"); compilations = new CompilationPair(compilationWithoutGeneratedDocuments, finalCompilation); - - // This should hopefully be safe to return as null. Because we already reached the 'FinalState' - // before, we should have already recorded the assembly symbols for it. So not recording them - // again is likely ok (as long as compilations continue to return the same IAssemblySymbols for - // the same references across source edits). - metadataReferenceToProjectId = null; return; } @@ -354,16 +335,13 @@ private void GetPartialCompilationState( compilationWithoutGeneratedDocuments, compilationWithoutGeneratedDocuments.AddSyntaxTrees(generatorInfo.Documents.States.Values.Select(state => state.GetSyntaxTree(cancellationToken)))); - // Now add in back a consistent set of project references. For project references - // try to get either a CompilationReference or a SkeletonReference. This ensures - // that the in-progress project only reports a reference to another project if it - // could actually get a reference to that project's metadata. + // Now add in back a consistent set of project references. For project references try to get either a + // CompilationReference or a SkeletonReference. This ensures that the in-progress project only reports a + // reference to another project if it could actually get a reference to that project's metadata. var metadataReferences = new List(); var newProjectReferences = new List(); metadataReferences.AddRange(this.ProjectState.MetadataReferences); - metadataReferenceToProjectId = []; - foreach (var projectReference in this.ProjectState.ProjectReferences) { var referencedProject = compilationState.SolutionState.GetProjectState(projectReference.ProjectId); @@ -396,7 +374,6 @@ private void GetPartialCompilationState( { newProjectReferences.Add(projectReference); metadataReferences.Add(metadata); - metadataReferenceToProjectId.Add(metadata, projectReference.ProjectId); } } } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.GeneratedFileReplacingCompilationTracker.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.GeneratedFileReplacingCompilationTracker.cs index 78f48b72040f5..d989a1cf14156 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.GeneratedFileReplacingCompilationTracker.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.GeneratedFileReplacingCompilationTracker.cs @@ -57,7 +57,7 @@ public ICompilationTracker Fork(ProjectState newProject, CompilationAndGenerator throw new NotImplementedException(); } - public ICompilationTracker FreezePartialStateWithTree(SolutionCompilationState compilationState, DocumentState docState, SyntaxTree tree, CancellationToken cancellationToken) + public ICompilationTracker FreezePartialStateWithDocument(SolutionCompilationState compilationState, DocumentState docState, CancellationToken cancellationToken) { // Because we override SourceGeneratedDocument.WithFrozenPartialSemantics directly, we shouldn't be able to get here. throw ExceptionUtilities.Unreachable(); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.ICompilationTracker.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.ICompilationTracker.cs index 947441af76664..4cfd8f54ab79c 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.ICompilationTracker.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.ICompilationTracker.cs @@ -35,7 +35,7 @@ private interface ICompilationTracker /// bool ContainsAssemblyOrModuleOrDynamic(ISymbol symbol, bool primary); ICompilationTracker Fork(ProjectState newProject, CompilationAndGeneratorDriverTranslationAction? translate); - ICompilationTracker FreezePartialStateWithTree(SolutionCompilationState compilationState, DocumentState docState, SyntaxTree tree, CancellationToken cancellationToken); + ICompilationTracker FreezePartialStateWithDocument(SolutionCompilationState compilationState, DocumentState docState, CancellationToken cancellationToken); Task GetCompilationAsync(SolutionCompilationState compilationState, CancellationToken cancellationToken); Task GetDependentVersionAsync(SolutionCompilationState compilationState, CancellationToken cancellationToken); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs index 0c25552a2d56e..a6bd86fc8ea44 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs @@ -1068,7 +1068,7 @@ static SolutionCompilationState WithFrozenPartialCompilationIncludingSpecificDoc try { var allDocumentIds = @this.SolutionState.GetRelatedDocumentIds(documentId); - using var _ = ArrayBuilder<(DocumentState, SyntaxTree)>.GetInstance(allDocumentIds.Length, out var statesAndTrees); + using var _ = ArrayBuilder.GetInstance(allDocumentIds.Length, out var documentStates); // We grab all the contents of linked files as well to ensure that our snapshot is correct wrt to the // set of linked document ids our state says are in it. Note: all of these trees should share the same @@ -1077,14 +1077,14 @@ static SolutionCompilationState WithFrozenPartialCompilationIncludingSpecificDoc foreach (var currentDocumentId in allDocumentIds) { var documentState = @this.SolutionState.GetRequiredDocumentState(currentDocumentId); - statesAndTrees.Add((documentState, documentState.GetSyntaxTree(cancellationToken))); + documentStates.Add(documentState); } using (@this.StateLock.DisposableWait(cancellationToken)) { if (!@this._cachedFrozenDocumentState.TryGetValue(documentId, out var compilationState)) { - compilationState = ComputeFrozenPartialState(@this, statesAndTrees, cancellationToken); + compilationState = ComputeFrozenPartialState(@this, documentStates, cancellationToken); @this._cachedFrozenDocumentState.Add(documentId, compilationState); } @@ -1099,17 +1099,17 @@ static SolutionCompilationState WithFrozenPartialCompilationIncludingSpecificDoc static SolutionCompilationState ComputeFrozenPartialState( SolutionCompilationState @this, - ArrayBuilder<(DocumentState, SyntaxTree)> statesAndTrees, + ArrayBuilder documentStates, CancellationToken cancellationToken) { var newIdToProjectStateMap = @this.SolutionState.ProjectStates; var newIdToTrackerMap = @this._projectIdToTrackerMap; - foreach (var (docState, tree) in statesAndTrees) + foreach (var docState in documentStates) { // if we don't have one or it is stale, create a new partial solution var tracker = @this.GetCompilationTracker(docState.Id.ProjectId); - var newTracker = tracker.FreezePartialStateWithTree(@this, docState, tree, cancellationToken); + var newTracker = tracker.FreezePartialStateWithDocument(@this, docState, cancellationToken); Contract.ThrowIfFalse(newIdToProjectStateMap.ContainsKey(docState.Id.ProjectId)); newIdToProjectStateMap = newIdToProjectStateMap.SetItem(docState.Id.ProjectId, newTracker.ProjectState); diff --git a/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs b/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs index 6c6a16400d9ca..18046009baed0 100644 --- a/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs +++ b/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs @@ -3647,13 +3647,13 @@ public async Task TestFrozenPartialSemanticsHandlesRemoveAndAddWithNullPathAndDi project = project.RemoveDocument(project.DocumentIds.Single()) .AddDocument("RegularDocument2.cs", "// Source File", filePath: null).Project; - // Freeze semantics -- with the new document; this should still give us a project with two documents: the new - // one will be added, and the old one will stay around since the name differed. + // Freeze semantics -- with the new document; this should still give us a project with one document: just + // the newly added one. var frozenDocument = project.Documents.Single().WithFrozenPartialSemantics(CancellationToken.None); - Assert.Equal(2, frozenDocument.Project.Documents.Count()); + Assert.Equal(1, frozenDocument.Project.Documents.Count()); var treesInCompilation = (await frozenDocument.Project.GetCompilationAsync()).SyntaxTrees; - Assert.Equal(2, treesInCompilation.Count()); + Assert.Equal(1, treesInCompilation.Count()); foreach (var document in frozenDocument.Project.Documents) Assert.Contains(await document.GetRequiredSyntaxTreeAsync(CancellationToken.None), treesInCompilation); From 39dff432afb353c6c1e0d268b6bcd4e9cb202d1b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 13 Feb 2024 12:24:44 -0800 Subject: [PATCH 02/13] simplify --- .../SolutionCompilationState.CompilationTracker.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs index 76015ed0c878d..af10060599c11 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs @@ -202,12 +202,7 @@ public ICompilationTracker FreezePartialStateWithDocument( var builder = ImmutableList.CreateBuilder<(ProjectState, CompilationAndGeneratorDriverTranslationAction)>(); - if (inProgressProject.DocumentStates.TryGetState(docState.Id, out var oldState) && oldState == docState) - { - // Existing state matches the state we're trying to move to. No work to do. Just freeze us at our - // current position. - } - else + if (!inProgressProject.DocumentStates.TryGetState(docState.Id, out var oldState) || oldState != docState) { // We do not have the exact document. It either means this document was recently added, or the // document was recently changed. We now need to update both the inProgressState and the From 85ba6203b9a2bab1ff289b672fd15e97205fd5bc Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 13 Feb 2024 12:27:50 -0800 Subject: [PATCH 03/13] docs --- ...SolutionCompilationState.CompilationTracker.cs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs index af10060599c11..1c902fbd8274a 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs @@ -200,7 +200,7 @@ public ICompilationTracker FreezePartialStateWithDocument( out var generatorInfo, cancellationToken); - var builder = ImmutableList.CreateBuilder<(ProjectState, CompilationAndGeneratorDriverTranslationAction)>(); + var pendingActions = ImmutableList<(ProjectState, CompilationAndGeneratorDriverTranslationAction)>.Empty; if (!inProgressProject.DocumentStates.TryGetState(docState.Id, out var oldState) || oldState != docState) { @@ -225,7 +225,7 @@ public ICompilationTracker FreezePartialStateWithDocument( // our current state. Note if no compilation existed GetPartialCompilationState would have // produced an empty one, and removed any documents, so inProgressProject.DocumentStates would // have been empty originally. - builder.Add((inProgressProject, new CompilationAndGeneratorDriverTranslationAction.TouchDocumentAction(oldState, docState))); + pendingActions = [(inProgressProject, new CompilationAndGeneratorDriverTranslationAction.TouchDocumentAction(oldState, docState))]; inProgressProject = inProgressProject.UpdateDocument(docState, contentChanged: true); } else @@ -237,7 +237,7 @@ public ICompilationTracker FreezePartialStateWithDocument( if (oldState is null) { // Scenario 2. - builder.Add((inProgressProject, new CompilationAndGeneratorDriverTranslationAction.AddDocumentsAction([docState]))); + pendingActions = [(inProgressProject, new CompilationAndGeneratorDriverTranslationAction.AddDocumentsAction([docState]))]; inProgressProject = inProgressProject.AddDocuments([docState]); } else @@ -245,8 +245,9 @@ public ICompilationTracker FreezePartialStateWithDocument( // Scenario 3. The old doc came from some other document with a different ID then we started // with -- if the document ID still existed we would have been in the Scenario 1 case // instead. We'll find the old document ID, remove that state, and then add ours. - builder.Add((inProgressProject, new CompilationAndGeneratorDriverTranslationAction.RemoveDocumentsAction([oldState]))); - builder.Add((inProgressProject, new CompilationAndGeneratorDriverTranslationAction.AddDocumentsAction([docState]))); + pendingActions = [ + (inProgressProject, new CompilationAndGeneratorDriverTranslationAction.RemoveDocumentsAction([oldState])), + (inProgressProject, new CompilationAndGeneratorDriverTranslationAction.AddDocumentsAction([docState]))]; inProgressProject = inProgressProject .RemoveDocuments([oldState.Id]) .AddDocuments([docState]); @@ -254,12 +255,14 @@ public ICompilationTracker FreezePartialStateWithDocument( } } + // Transition us to a frozen in progress state. With the best compilations up to this point, and the + // pending actions we'll need to perform on it to get to the final state. var inProgressState = InProgressState.Create( isFrozen: true, compilationPair.CompilationWithoutGeneratedDocuments, generatorInfo, compilationPair.CompilationWithGeneratedDocuments, - builder.ToImmutable()); + pendingActions); return new CompilationTracker(inProgressProject, inProgressState, this.SkeletonReferenceCache.Clone()); } From 0d03232da5ce8c85cf3a5b98b95d335302299e7d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 13 Feb 2024 12:41:30 -0800 Subject: [PATCH 04/13] preserve --- ...pilationTracker.CompilationTrackerState.cs | 10 ++ ...tionCompilationState.CompilationTracker.cs | 139 ++++++++++-------- .../SolutionWithSourceGeneratorTests.cs | 4 +- 3 files changed, 90 insertions(+), 63 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.CompilationTrackerState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.CompilationTrackerState.cs index f391116d7b16e..94d82c46010fd 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.CompilationTrackerState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.CompilationTrackerState.cs @@ -205,6 +205,16 @@ public static FinalCompilationTrackerState Create( unrootedSymbolSet); } + public FinalCompilationTrackerState WithIsFrozen() + => new(isFrozen: true, + FinalCompilationWithGeneratedDocuments, + CompilationWithoutGeneratedDocuments, + // As a policy, all partial-state projects are said to have incomplete references, since the + // state has no guarantees. + hasSuccessfullyLoaded: false, + GeneratorInfo, + UnrootedSymbolSet); + private static void RecordAssemblySymbols(ProjectId projectId, Compilation compilation, Dictionary? metadataReferenceToProjectId) { RecordSourceOfAssemblySymbol(compilation.Assembly, projectId); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs index 1c902fbd8274a..1373fae80d34e 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs @@ -192,79 +192,94 @@ public ICompilationTracker FreezePartialStateWithDocument( DocumentState docState, CancellationToken cancellationToken) { - GetPartialCompilationState( - compilationState, - docState.Id, - out var inProgressProject, - out var compilationPair, - out var generatorInfo, - cancellationToken); - - var pendingActions = ImmutableList<(ProjectState, CompilationAndGeneratorDriverTranslationAction)>.Empty; - - if (!inProgressProject.DocumentStates.TryGetState(docState.Id, out var oldState) || oldState != docState) + // If we're already finalized, and no change has been made to this document. Then just return what we + // have (just with the frozen bit flipped so that any future forks keep things frozen). + var state = this.ReadState(); + if (state is FinalCompilationTrackerState finalState && + this.ProjectState.DocumentStates.TryGetState(docState.Id, out var oldState) && + oldState == docState) { - // We do not have the exact document. It either means this document was recently added, or the - // document was recently changed. We now need to update both the inProgressState and the - // compilation. There are several possibilities we want to consider: - // - // 1. An earlier version of the document is present in the compilation, and we just need to update it to the current version - // 2. The document wasn't present in the original snapshot at all, and we just need to add the - // document. - // 3. The document wasn't present in the original snapshot, but an older file had been removed that - // had the same file path. As a heuristic, we remove the old one so we don't end up with - // duplicate documents. - // - // Note it's possible that we simply had never tried to produce a compilation yet for this project - // at all, in that case GetPartialCompilationState would have produced an empty compilation, and it - // would have updated inProgressProject to remove all the documents. Thus, that is no different than - // the "add" case above. - if (oldState != null) - { - // Scenario 1. The document had been previously parsed and it's there, so we can update it with - // our current state. Note if no compilation existed GetPartialCompilationState would have - // produced an empty one, and removed any documents, so inProgressProject.DocumentStates would - // have been empty originally. - pendingActions = [(inProgressProject, new CompilationAndGeneratorDriverTranslationAction.TouchDocumentAction(oldState, docState))]; - inProgressProject = inProgressProject.UpdateDocument(docState, contentChanged: true); - } - else + return new CompilationTracker(this.ProjectState, finalState.WithIsFrozen(), this.SkeletonReferenceCache.Clone()); + } + else + { + // Otherwise, we're not finalized, or the document has changed. We'll create an in-progress state + // to represent the new state of the project, with all the necessary translation steps to + // incorporate the new document. + GetPartialCompilationState( + compilationState, + docState.Id, + out var inProgressProject, + out var compilationPair, + out var generatorInfo, + cancellationToken); + + var pendingActions = ImmutableList<(ProjectState, CompilationAndGeneratorDriverTranslationAction)>.Empty; + + if (!inProgressProject.DocumentStates.TryGetState(docState.Id, out oldState) || oldState != docState) { - // We're in either scenario 2 or 3. Do we have an existing document to try replacing? Note: the - // file path here corresponds to Document.FilePath. If a document's file path is null, we then - // substitute Document.Name, so we usually expect there to be a unique string regardless. - oldState = inProgressProject.DocumentStates.States.FirstOrDefault(kvp => kvp.Value.FilePath == docState.FilePath).Value; - if (oldState is null) + // We do not have the exact document. It either means this document was recently added, or the + // document was recently changed. We now need to update both the inProgressState and the + // compilation. There are several possibilities we want to consider: + // + // 1. An earlier version of the document is present in the compilation, and we just need to update it to the current version + // 2. The document wasn't present in the original snapshot at all, and we just need to add the + // document. + // 3. The document wasn't present in the original snapshot, but an older file had been removed that + // had the same file path. As a heuristic, we remove the old one so we don't end up with + // duplicate documents. + // + // Note it's possible that we simply had never tried to produce a compilation yet for this project + // at all, in that case GetPartialCompilationState would have produced an empty compilation, and it + // would have updated inProgressProject to remove all the documents. Thus, that is no different than + // the "add" case above. + if (oldState != null) { - // Scenario 2. - pendingActions = [(inProgressProject, new CompilationAndGeneratorDriverTranslationAction.AddDocumentsAction([docState]))]; - inProgressProject = inProgressProject.AddDocuments([docState]); + // Scenario 1. The document had been previously parsed and it's there, so we can update it with + // our current state. Note if no compilation existed GetPartialCompilationState would have + // produced an empty one, and removed any documents, so inProgressProject.DocumentStates would + // have been empty originally. + pendingActions = [(inProgressProject, new CompilationAndGeneratorDriverTranslationAction.TouchDocumentAction(oldState, docState))]; + inProgressProject = inProgressProject.UpdateDocument(docState, contentChanged: true); } else { - // Scenario 3. The old doc came from some other document with a different ID then we started - // with -- if the document ID still existed we would have been in the Scenario 1 case - // instead. We'll find the old document ID, remove that state, and then add ours. - pendingActions = [ - (inProgressProject, new CompilationAndGeneratorDriverTranslationAction.RemoveDocumentsAction([oldState])), + // We're in either scenario 2 or 3. Do we have an existing document to try replacing? Note: the + // file path here corresponds to Document.FilePath. If a document's file path is null, we then + // substitute Document.Name, so we usually expect there to be a unique string regardless. + oldState = inProgressProject.DocumentStates.States.FirstOrDefault(kvp => kvp.Value.FilePath == docState.FilePath).Value; + if (oldState is null) + { + // Scenario 2. + pendingActions = [(inProgressProject, new CompilationAndGeneratorDriverTranslationAction.AddDocumentsAction([docState]))]; + inProgressProject = inProgressProject.AddDocuments([docState]); + } + else + { + // Scenario 3. The old doc came from some other document with a different ID then we started + // with -- if the document ID still existed we would have been in the Scenario 1 case + // instead. We'll find the old document ID, remove that state, and then add ours. + pendingActions = [ + (inProgressProject, new CompilationAndGeneratorDriverTranslationAction.RemoveDocumentsAction([oldState])), (inProgressProject, new CompilationAndGeneratorDriverTranslationAction.AddDocumentsAction([docState]))]; - inProgressProject = inProgressProject - .RemoveDocuments([oldState.Id]) - .AddDocuments([docState]); + inProgressProject = inProgressProject + .RemoveDocuments([oldState.Id]) + .AddDocuments([docState]); + } } } - } - // Transition us to a frozen in progress state. With the best compilations up to this point, and the - // pending actions we'll need to perform on it to get to the final state. - var inProgressState = InProgressState.Create( - isFrozen: true, - compilationPair.CompilationWithoutGeneratedDocuments, - generatorInfo, - compilationPair.CompilationWithGeneratedDocuments, - pendingActions); + // Transition us to a frozen in progress state. With the best compilations up to this point, and the + // pending actions we'll need to perform on it to get to the final state. + var inProgressState = InProgressState.Create( + isFrozen: true, + compilationPair.CompilationWithoutGeneratedDocuments, + generatorInfo, + compilationPair.CompilationWithGeneratedDocuments, + pendingActions); - return new CompilationTracker(inProgressProject, inProgressState, this.SkeletonReferenceCache.Clone()); + return new CompilationTracker(inProgressProject, inProgressState, this.SkeletonReferenceCache.Clone()); + } } /// diff --git a/src/Workspaces/CoreTest/SolutionTests/SolutionWithSourceGeneratorTests.cs b/src/Workspaces/CoreTest/SolutionTests/SolutionWithSourceGeneratorTests.cs index 49385353c15b6..28e58850f686d 100644 --- a/src/Workspaces/CoreTest/SolutionTests/SolutionWithSourceGeneratorTests.cs +++ b/src/Workspaces/CoreTest/SolutionTests/SolutionWithSourceGeneratorTests.cs @@ -335,7 +335,9 @@ public async Task PartialCompilationsIncludeGeneratedFilesAfterFullGeneration(Te Assert.NotSame(partialProject, project); var partialCompilation = await partialProject.GetRequiredCompilationAsync(CancellationToken.None); - Assert.Same(fullCompilation, partialCompilation); + Assert.NotSame(fullCompilation, partialCompilation); + foreach (var tree in fullCompilation.SyntaxTrees.Skip(1)) + Assert.Contains(tree, partialCompilation.SyntaxTrees); } [Theory, CombinatorialData] From e3c6c2fa3f23646cb395ce79817a27bde80054f1 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 13 Feb 2024 12:42:37 -0800 Subject: [PATCH 05/13] fix --- .../Solution/SolutionCompilationState.CompilationTracker.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs index 1373fae80d34e..f955820f97b3b 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs @@ -261,7 +261,7 @@ public ICompilationTracker FreezePartialStateWithDocument( // instead. We'll find the old document ID, remove that state, and then add ours. pendingActions = [ (inProgressProject, new CompilationAndGeneratorDriverTranslationAction.RemoveDocumentsAction([oldState])), - (inProgressProject, new CompilationAndGeneratorDriverTranslationAction.AddDocumentsAction([docState]))]; + (inProgressProject, new CompilationAndGeneratorDriverTranslationAction.AddDocumentsAction([docState]))]; inProgressProject = inProgressProject .RemoveDocuments([oldState.Id]) .AddDocuments([docState]); From ae90a86e6b9b2544b6dcad51314a389f1ed6bace Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 13 Feb 2024 12:43:57 -0800 Subject: [PATCH 06/13] Update test --- .../Solution/SolutionCompilationState.CompilationTracker.cs | 6 +++--- .../SolutionTests/SolutionWithSourceGeneratorTests.cs | 4 +--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs index f955820f97b3b..436a94262b95d 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs @@ -259,11 +259,11 @@ public ICompilationTracker FreezePartialStateWithDocument( // Scenario 3. The old doc came from some other document with a different ID then we started // with -- if the document ID still existed we would have been in the Scenario 1 case // instead. We'll find the old document ID, remove that state, and then add ours. + var inProgressWithDocumentRemoved = inProgressProject.RemoveDocuments([oldState.Id]); pendingActions = [ (inProgressProject, new CompilationAndGeneratorDriverTranslationAction.RemoveDocumentsAction([oldState])), - (inProgressProject, new CompilationAndGeneratorDriverTranslationAction.AddDocumentsAction([docState]))]; - inProgressProject = inProgressProject - .RemoveDocuments([oldState.Id]) + (inProgressWithDocumentRemoved, new CompilationAndGeneratorDriverTranslationAction.AddDocumentsAction([docState]))]; + inProgressProject = inProgressWithDocumentRemoved .AddDocuments([docState]); } } diff --git a/src/Workspaces/CoreTest/SolutionTests/SolutionWithSourceGeneratorTests.cs b/src/Workspaces/CoreTest/SolutionTests/SolutionWithSourceGeneratorTests.cs index 28e58850f686d..49385353c15b6 100644 --- a/src/Workspaces/CoreTest/SolutionTests/SolutionWithSourceGeneratorTests.cs +++ b/src/Workspaces/CoreTest/SolutionTests/SolutionWithSourceGeneratorTests.cs @@ -335,9 +335,7 @@ public async Task PartialCompilationsIncludeGeneratedFilesAfterFullGeneration(Te Assert.NotSame(partialProject, project); var partialCompilation = await partialProject.GetRequiredCompilationAsync(CancellationToken.None); - Assert.NotSame(fullCompilation, partialCompilation); - foreach (var tree in fullCompilation.SyntaxTrees.Skip(1)) - Assert.Contains(tree, partialCompilation.SyntaxTrees); + Assert.Same(fullCompilation, partialCompilation); } [Theory, CombinatorialData] From 4ec51f614348e921f5956bd738f807fd45d21bc2 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 13 Feb 2024 14:30:36 -0800 Subject: [PATCH 07/13] Simplify --- ...pilationTracker.CompilationTrackerState.cs | 9 +- ...tionCompilationState.CompilationTracker.cs | 90 +++++-------------- 2 files changed, 29 insertions(+), 70 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.CompilationTrackerState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.CompilationTrackerState.cs index 94d82c46010fd..290f4573ca9ec 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.CompilationTrackerState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.CompilationTrackerState.cs @@ -166,7 +166,10 @@ private FinalCompilationTrackerState( : base(isFrozen, compilationWithoutGeneratedDocuments, generatorInfo) { Contract.ThrowIfNull(finalCompilationWithGeneratedDocuments); - HasSuccessfullyLoaded = hasSuccessfullyLoaded; + + // As a policy, all partial-state projects are said to have incomplete references, since the + // state has no guarantees. + HasSuccessfullyLoaded = hasSuccessfullyLoaded && !isFrozen; FinalCompilationWithGeneratedDocuments = finalCompilationWithGeneratedDocuments; UnrootedSymbolSet = unrootedSymbolSet; @@ -209,9 +212,7 @@ public FinalCompilationTrackerState WithIsFrozen() => new(isFrozen: true, FinalCompilationWithGeneratedDocuments, CompilationWithoutGeneratedDocuments, - // As a policy, all partial-state projects are said to have incomplete references, since the - // state has no guarantees. - hasSuccessfullyLoaded: false, + HasSuccessfullyLoaded, GeneratorInfo, UnrootedSymbolSet); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs index 436a94262b95d..873287a4247d7 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs @@ -192,6 +192,13 @@ public ICompilationTracker FreezePartialStateWithDocument( DocumentState docState, CancellationToken cancellationToken) { + GetPartialCompilationState( + docState.Id, + out var inProgressProject, + out var compilationPair, + out var generatorInfo, + cancellationToken); + // If we're already finalized, and no change has been made to this document. Then just return what we // have (just with the frozen bit flipped so that any future forks keep things frozen). var state = this.ReadState(); @@ -206,13 +213,6 @@ public ICompilationTracker FreezePartialStateWithDocument( // Otherwise, we're not finalized, or the document has changed. We'll create an in-progress state // to represent the new state of the project, with all the necessary translation steps to // incorporate the new document. - GetPartialCompilationState( - compilationState, - docState.Id, - out var inProgressProject, - out var compilationPair, - out var generatorInfo, - cancellationToken); var pendingActions = ImmutableList<(ProjectState, CompilationAndGeneratorDriverTranslationAction)>.Empty; @@ -290,7 +290,6 @@ public ICompilationTracker FreezePartialStateWithDocument( /// returned will have a compilation that is retained so that it cannot disappear. /// private void GetPartialCompilationState( - SolutionCompilationState compilationState, DocumentId id, out ProjectState inProgressProject, out CompilationPair compilations, @@ -347,57 +346,6 @@ private void GetPartialCompilationState( compilations = new CompilationPair( compilationWithoutGeneratedDocuments, compilationWithoutGeneratedDocuments.AddSyntaxTrees(generatorInfo.Documents.States.Values.Select(state => state.GetSyntaxTree(cancellationToken)))); - - // Now add in back a consistent set of project references. For project references try to get either a - // CompilationReference or a SkeletonReference. This ensures that the in-progress project only reports a - // reference to another project if it could actually get a reference to that project's metadata. - var metadataReferences = new List(); - var newProjectReferences = new List(); - metadataReferences.AddRange(this.ProjectState.MetadataReferences); - - foreach (var projectReference in this.ProjectState.ProjectReferences) - { - var referencedProject = compilationState.SolutionState.GetProjectState(projectReference.ProjectId); - if (referencedProject != null) - { - if (referencedProject.IsSubmission) - { - var previousScriptCompilation = compilationState.GetCompilationAsync( - projectReference.ProjectId, cancellationToken).WaitAndGetResult(cancellationToken); - - // previous submission project must support compilation: - RoslynDebug.Assert(previousScriptCompilation != null); - - compilations = compilations.WithPreviousScriptCompilation(previousScriptCompilation); - } - else - { - // get the latest metadata for the partial compilation of the referenced project. - var metadata = compilationState.GetPartialMetadataReference(projectReference, this.ProjectState); - - if (metadata == null) - { - // if we failed to get the metadata, check to see if we previously had existing metadata and reuse it instead. - var inProgressCompilationNotRef = compilations.CompilationWithGeneratedDocuments; - metadata = inProgressCompilationNotRef.ExternalReferences.FirstOrDefault( - r => SolutionCompilationState.GetProjectId(inProgressCompilationNotRef.GetAssemblyOrModuleSymbol(r) as IAssemblySymbol) == projectReference.ProjectId); - } - - if (metadata != null) - { - newProjectReferences.Add(projectReference); - metadataReferences.Add(metadata); - } - } - } - } - - inProgressProject = inProgressProject.WithProjectReferences(newProjectReferences); - - if (!Enumerable.SequenceEqual(compilations.CompilationWithoutGeneratedDocuments.ExternalReferences, metadataReferences)) - { - compilations = compilations.WithReferences(metadataReferences); - } } private static bool IsTouchDocumentActionForDocument(CompilationAndGeneratorDriverTranslationAction action, DocumentId id) @@ -691,18 +639,28 @@ await compilationState.GetCompilationAsync( } else { - var metadataReference = await compilationState.GetMetadataReferenceAsync( - projectReference, this.ProjectState, cancellationToken).ConfigureAwait(false); + var metadataReference = isFrozen + ? compilationState.GetPartialMetadataReference(projectReference, this.ProjectState) + : await compilationState.GetMetadataReferenceAsync(projectReference, this.ProjectState, cancellationToken).ConfigureAwait(false); + + if (metadataReference is null && isFrozen) + { + // if we failed to get the metadata and we were frozen, check to see if we + // previously had existing metadata and reuse it instead. + var inProgressCompilationNotRef = staleCompilationWithGeneratedDocuments ?? compilationWithoutGeneratedDocuments; + metadataReference = inProgressCompilationNotRef.ExternalReferences.FirstOrDefault( + r => GetProjectId(inProgressCompilationNotRef.GetAssemblyOrModuleSymbol(r) as IAssemblySymbol) == projectReference.ProjectId); + } // A reference can fail to be created if a skeleton assembly could not be constructed. - if (metadataReference != null) + if (metadataReference == null) { - newReferences.Add(metadataReference); - metadataReferenceToProjectId.Add(metadataReference, projectReference.ProjectId); + hasSuccessfullyLoaded = false; } else { - hasSuccessfullyLoaded = false; + newReferences.Add(metadataReference); + metadataReferenceToProjectId.Add(metadataReference, projectReference.ProjectId); } } } @@ -735,7 +693,7 @@ await compilationState.GetCompilationAsync( isFrozen, compilationWithGeneratedDocuments, compilationWithoutGeneratedDocuments, - hasSuccessfullyLoaded, + hasSuccessfullyLoaded && !isFrozen, nextGeneratorInfo, this.ProjectState.Id, metadataReferenceToProjectId); From ac5b22002bf26f6dff752fc2c77fb7fd76e0ebb0 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 13 Feb 2024 14:32:31 -0800 Subject: [PATCH 08/13] Simplify --- .../Solution/SolutionCompilationState.CompilationTracker.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs index 873287a4247d7..55644dd666855 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs @@ -206,7 +206,8 @@ public ICompilationTracker FreezePartialStateWithDocument( this.ProjectState.DocumentStates.TryGetState(docState.Id, out var oldState) && oldState == docState) { - return new CompilationTracker(this.ProjectState, finalState.WithIsFrozen(), this.SkeletonReferenceCache.Clone()); + var frozenFinalState = finalState.WithIsFrozen(); + return new CompilationTracker(this.ProjectState, frozenFinalState, this.SkeletonReferenceCache.Clone()); } else { From 6c7fdb7dd95e7f239b9215386dea304b905d8413 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 13 Feb 2024 14:34:06 -0800 Subject: [PATCH 09/13] Update src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs --- .../Solution/SolutionCompilationState.CompilationTracker.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs index 55644dd666855..6db99dc7374f4 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs @@ -694,7 +694,7 @@ await compilationState.GetCompilationAsync( isFrozen, compilationWithGeneratedDocuments, compilationWithoutGeneratedDocuments, - hasSuccessfullyLoaded && !isFrozen, + hasSuccessfullyLoaded, nextGeneratorInfo, this.ProjectState.Id, metadataReferenceToProjectId); From 4e980246ae94c1d831d7567a72fd5c3ecbd1cdd3 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 13 Feb 2024 19:20:03 -0800 Subject: [PATCH 10/13] Simplify --- .../Solution/SolutionCompilationState.CompilationTracker.cs | 5 +---- ...ilationState.GeneratedFileReplacingCompilationTracker.cs | 6 +++--- .../SolutionCompilationState.ICompilationTracker.cs | 4 ++-- .../Portable/Workspace/Solution/SolutionCompilationState.cs | 4 ++-- 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs index 69281acb07f38..b88d15391f4fa 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs @@ -187,15 +187,12 @@ public ICompilationTracker Fork( } } - public ICompilationTracker FreezePartialState( - SolutionCompilationState compilationState, - CancellationToken cancellationToken) + public ICompilationTracker FreezePartialState(CancellationToken cancellationToken) { return FreezePartialStateWorker(docState: null, cancellationToken); } public ICompilationTracker FreezePartialStateWithDocument( - SolutionCompilationState compilationState, DocumentState docState, CancellationToken cancellationToken) { diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.GeneratedFileReplacingCompilationTracker.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.GeneratedFileReplacingCompilationTracker.cs index 4c74666b0e6f6..bbb9c98184d4f 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.GeneratedFileReplacingCompilationTracker.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.GeneratedFileReplacingCompilationTracker.cs @@ -57,13 +57,13 @@ public ICompilationTracker Fork(ProjectState newProject, CompilationAndGenerator throw new NotImplementedException(); } - public ICompilationTracker FreezePartialState(SolutionCompilationState compilationState, CancellationToken cancellationToken) + public ICompilationTracker FreezePartialState(CancellationToken cancellationToken) { // Ensure the underlying tracker is totally frozen, and then ensure our replaced generated doc is present. - return new GeneratedFileReplacingCompilationTracker(UnderlyingTracker.FreezePartialState(compilationState, cancellationToken), replacementDocumentState); + return new GeneratedFileReplacingCompilationTracker(UnderlyingTracker.FreezePartialState(cancellationToken), replacementDocumentState); } - public ICompilationTracker FreezePartialStateWithDocument(SolutionCompilationState compilationState, DocumentState docState, CancellationToken cancellationToken) + public ICompilationTracker FreezePartialStateWithDocument(DocumentState docState, CancellationToken cancellationToken) { // Because we override SourceGeneratedDocument.WithFrozenPartialSemantics directly, we shouldn't be able to get here. throw ExceptionUtilities.Unreachable(); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.ICompilationTracker.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.ICompilationTracker.cs index 6903b90319943..4467f075f719d 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.ICompilationTracker.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.ICompilationTracker.cs @@ -38,8 +38,8 @@ private interface ICompilationTracker Task GetCompilationAsync(SolutionCompilationState compilationState, CancellationToken cancellationToken); - ICompilationTracker FreezePartialState(SolutionCompilationState compilationState, CancellationToken cancellationToken); - ICompilationTracker FreezePartialStateWithDocument(SolutionCompilationState compilationState, DocumentState docState, CancellationToken cancellationToken); + ICompilationTracker FreezePartialState(CancellationToken cancellationToken); + ICompilationTracker FreezePartialStateWithDocument(DocumentState docState, CancellationToken cancellationToken); Task GetDependentVersionAsync(SolutionCompilationState compilationState, CancellationToken cancellationToken); Task GetDependentSemanticVersionAsync(SolutionCompilationState compilationState, CancellationToken cancellationToken); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs index d698b3f21239a..86710f5a8bb41 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs @@ -1025,7 +1025,7 @@ private SolutionCompilationState ComputeFrozenSnapshot(CancellationToken cancell { // if we don't have one or it is stale, create a new partial solution var tracker = GetCompilationTracker(projectId); - var newTracker = tracker.FreezePartialState(this, cancellationToken); + var newTracker = tracker.FreezePartialState(cancellationToken); Contract.ThrowIfFalse(newIdToProjectStateMapBuilder.ContainsKey(projectId)); newIdToProjectStateMapBuilder[projectId] = newTracker.ProjectState; @@ -1151,7 +1151,7 @@ static SolutionCompilationState ComputeFrozenPartialState( { // if we don't have one or it is stale, create a new partial solution var tracker = @this.GetCompilationTracker(docState.Id.ProjectId); - var newTracker = tracker.FreezePartialStateWithDocument(@this, docState, cancellationToken); + var newTracker = tracker.FreezePartialStateWithDocument(docState, cancellationToken); Contract.ThrowIfFalse(newIdToProjectStateMap.ContainsKey(docState.Id.ProjectId)); newIdToProjectStateMap = newIdToProjectStateMap.SetItem(docState.Id.ProjectId, newTracker.ProjectState); From 52a13bea99cd01931f2e8eb70a06794aa8adc188 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 13 Feb 2024 19:25:13 -0800 Subject: [PATCH 11/13] Separate methods --- ...tionCompilationState.CompilationTracker.cs | 161 ++++++++++-------- 1 file changed, 89 insertions(+), 72 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs index b88d15391f4fa..3473845f89066 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs @@ -189,96 +189,112 @@ public ICompilationTracker Fork( public ICompilationTracker FreezePartialState(CancellationToken cancellationToken) { - return FreezePartialStateWorker(docState: null, cancellationToken); + GetPartialCompilationState( + documentId: null, + out var state, + out var inProgressProject, + out var compilationPair, + out var generatorInfo, + cancellationToken); + + // If we're already finalized then just return what we have, and with the frozen bit flipped so that + // any future forks keep things frozen. + if (state is FinalCompilationTrackerState finalState) + { + var frozenFinalState = finalState.WithIsFrozen(); + return new CompilationTracker(this.ProjectState, frozenFinalState, this.SkeletonReferenceCache.Clone()); + } + else + { + // Transition us to a frozen in progress state. With the best compilations up to this point, but no + // more pending actions, and with the frozen bit flipped so that any future forks keep things + // frozen. + var inProgressState = InProgressState.Create( + isFrozen: true, + compilationPair.CompilationWithoutGeneratedDocuments, + generatorInfo, + compilationPair.CompilationWithGeneratedDocuments, + ImmutableList<(ProjectState, CompilationAndGeneratorDriverTranslationAction)>.Empty); + + return new CompilationTracker(inProgressProject, inProgressState, this.SkeletonReferenceCache.Clone()); + } } public ICompilationTracker FreezePartialStateWithDocument( DocumentState docState, CancellationToken cancellationToken) - { - return FreezePartialStateWorker(docState, cancellationToken); - } - - private ICompilationTracker FreezePartialStateWorker( - DocumentState? docState, - CancellationToken cancellationToken) { GetPartialCompilationState( - docState?.Id, + docState.Id, + out var state, out var inProgressProject, out var compilationPair, out var generatorInfo, cancellationToken); + // If we're already finalized, and no change has been made to this document. Then just return what we + // have (just with the frozen bit flipped so that any future forks keep things frozen). + if (state is FinalCompilationTrackerState finalState && + this.ProjectState.DocumentStates.TryGetState(docState.Id, out var oldState) && + oldState == docState) + { + var frozenFinalState = finalState.WithIsFrozen(); + return new CompilationTracker(this.ProjectState, frozenFinalState, this.SkeletonReferenceCache.Clone()); + } + + // Otherwise, we're not finalized, or the document has changed. We'll create an in-progress state + // to represent the new state of the project, with all the necessary translation steps to + // incorporate the new document. var pendingActions = ImmutableList<(ProjectState, CompilationAndGeneratorDriverTranslationAction)>.Empty; - if (docState != null) + if (!inProgressProject.DocumentStates.TryGetState(docState.Id, out oldState) || oldState != docState) { - // If we're already finalized, and no change has been made to this document. Then just return what we - // have (just with the frozen bit flipped so that any future forks keep things frozen). - var state = this.ReadState(); - if (state is FinalCompilationTrackerState finalState && - this.ProjectState.DocumentStates.TryGetState(docState.Id, out var oldState) && - oldState == docState) + // We do not have the exact document. It either means this document was recently added, or the + // document was recently changed. We now need to update both the inProgressState and the + // compilation. There are several possibilities we want to consider: + // + // 1. An earlier version of the document is present in the compilation, and we just need to update it to the current version + // 2. The document wasn't present in the original snapshot at all, and we just need to add the + // document. + // 3. The document wasn't present in the original snapshot, but an older file had been removed that + // had the same file path. As a heuristic, we remove the old one so we don't end up with + // duplicate documents. + // + // Note it's possible that we simply had never tried to produce a compilation yet for this project + // at all, in that case GetPartialCompilationState would have produced an empty compilation, and it + // would have updated inProgressProject to remove all the documents. Thus, that is no different than + // the "add" case above. + if (oldState != null) { - var frozenFinalState = finalState.WithIsFrozen(); - return new CompilationTracker(this.ProjectState, frozenFinalState, this.SkeletonReferenceCache.Clone()); + // Scenario 1. The document had been previously parsed and it's there, so we can update it with + // our current state. Note if no compilation existed GetPartialCompilationState would have + // produced an empty one, and removed any documents, so inProgressProject.DocumentStates would + // have been empty originally. + pendingActions = [(inProgressProject, new CompilationAndGeneratorDriverTranslationAction.TouchDocumentAction(oldState, docState))]; + inProgressProject = inProgressProject.UpdateDocument(docState, contentChanged: true); } else { - // Otherwise, we're not finalized, or the document has changed. We'll create an in-progress state - // to represent the new state of the project, with all the necessary translation steps to - // incorporate the new document. - if (!inProgressProject.DocumentStates.TryGetState(docState.Id, out oldState) || oldState != docState) + // We're in either scenario 2 or 3. Do we have an existing document to try replacing? Note: the + // file path here corresponds to Document.FilePath. If a document's file path is null, we then + // substitute Document.Name, so we usually expect there to be a unique string regardless. + oldState = inProgressProject.DocumentStates.States.FirstOrDefault(kvp => kvp.Value.FilePath == docState.FilePath).Value; + if (oldState is null) { - // We do not have the exact document. It either means this document was recently added, or the - // document was recently changed. We now need to update both the inProgressState and the - // compilation. There are several possibilities we want to consider: - // - // 1. An earlier version of the document is present in the compilation, and we just need to update it to the current version - // 2. The document wasn't present in the original snapshot at all, and we just need to add the - // document. - // 3. The document wasn't present in the original snapshot, but an older file had been removed that - // had the same file path. As a heuristic, we remove the old one so we don't end up with - // duplicate documents. - // - // Note it's possible that we simply had never tried to produce a compilation yet for this project - // at all, in that case GetPartialCompilationState would have produced an empty compilation, and it - // would have updated inProgressProject to remove all the documents. Thus, that is no different than - // the "add" case above. - if (oldState != null) - { - // Scenario 1. The document had been previously parsed and it's there, so we can update it with - // our current state. Note if no compilation existed GetPartialCompilationState would have - // produced an empty one, and removed any documents, so inProgressProject.DocumentStates would - // have been empty originally. - pendingActions = [(inProgressProject, new CompilationAndGeneratorDriverTranslationAction.TouchDocumentAction(oldState, docState))]; - inProgressProject = inProgressProject.UpdateDocument(docState, contentChanged: true); - } - else - { - // We're in either scenario 2 or 3. Do we have an existing document to try replacing? Note: the - // file path here corresponds to Document.FilePath. If a document's file path is null, we then - // substitute Document.Name, so we usually expect there to be a unique string regardless. - oldState = inProgressProject.DocumentStates.States.FirstOrDefault(kvp => kvp.Value.FilePath == docState.FilePath).Value; - if (oldState is null) - { - // Scenario 2. - pendingActions = [(inProgressProject, new CompilationAndGeneratorDriverTranslationAction.AddDocumentsAction([docState]))]; - inProgressProject = inProgressProject.AddDocuments([docState]); - } - else - { - // Scenario 3. The old doc came from some other document with a different ID then we started - // with -- if the document ID still existed we would have been in the Scenario 1 case - // instead. We'll find the old document ID, remove that state, and then add ours. - var inProgressWithDocumentRemoved = inProgressProject.RemoveDocuments([oldState.Id]); - pendingActions = [ - (inProgressProject, new CompilationAndGeneratorDriverTranslationAction.RemoveDocumentsAction([oldState])), - (inProgressWithDocumentRemoved, new CompilationAndGeneratorDriverTranslationAction.AddDocumentsAction([docState]))]; - inProgressProject = inProgressWithDocumentRemoved - .AddDocuments([docState]); - } - } + // Scenario 2. + pendingActions = [(inProgressProject, new CompilationAndGeneratorDriverTranslationAction.AddDocumentsAction([docState]))]; + inProgressProject = inProgressProject.AddDocuments([docState]); + } + else + { + // Scenario 3. The old doc came from some other document with a different ID then we started + // with -- if the document ID still existed we would have been in the Scenario 1 case + // instead. We'll find the old document ID, remove that state, and then add ours. + var inProgressWithDocumentRemoved = inProgressProject.RemoveDocuments([oldState.Id]); + pendingActions = [ + (inProgressProject, new CompilationAndGeneratorDriverTranslationAction.RemoveDocumentsAction([oldState])), + (inProgressWithDocumentRemoved, new CompilationAndGeneratorDriverTranslationAction.AddDocumentsAction([docState]))]; + inProgressProject = inProgressWithDocumentRemoved + .AddDocuments([docState]); } } } @@ -304,12 +320,13 @@ private ICompilationTracker FreezePartialStateWorker( /// private void GetPartialCompilationState( DocumentId? documentId, + out CompilationTrackerState? state, out ProjectState inProgressProject, out CompilationPair compilations, out CompilationTrackerGeneratorInfo generatorInfo, CancellationToken cancellationToken) { - var state = ReadState(); + state = ReadState(); var compilationWithoutGeneratedDocuments = state?.CompilationWithoutGeneratedDocuments; generatorInfo = state?.GeneratorInfo ?? CompilationTrackerGeneratorInfo.Empty; From 9db05f562689139b3284ba84fec5fd945f298898 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 14 Feb 2024 10:12:48 -0800 Subject: [PATCH 12/13] indent --- ...tionCompilationState.CompilationTracker.cs | 116 +++++++++--------- 1 file changed, 59 insertions(+), 57 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs index 3473845f89066..fb21484415fea 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs @@ -241,74 +241,76 @@ public ICompilationTracker FreezePartialStateWithDocument( var frozenFinalState = finalState.WithIsFrozen(); return new CompilationTracker(this.ProjectState, frozenFinalState, this.SkeletonReferenceCache.Clone()); } - - // Otherwise, we're not finalized, or the document has changed. We'll create an in-progress state - // to represent the new state of the project, with all the necessary translation steps to - // incorporate the new document. - var pendingActions = ImmutableList<(ProjectState, CompilationAndGeneratorDriverTranslationAction)>.Empty; - if (!inProgressProject.DocumentStates.TryGetState(docState.Id, out oldState) || oldState != docState) + else { - // We do not have the exact document. It either means this document was recently added, or the - // document was recently changed. We now need to update both the inProgressState and the - // compilation. There are several possibilities we want to consider: - // - // 1. An earlier version of the document is present in the compilation, and we just need to update it to the current version - // 2. The document wasn't present in the original snapshot at all, and we just need to add the - // document. - // 3. The document wasn't present in the original snapshot, but an older file had been removed that - // had the same file path. As a heuristic, we remove the old one so we don't end up with - // duplicate documents. - // - // Note it's possible that we simply had never tried to produce a compilation yet for this project - // at all, in that case GetPartialCompilationState would have produced an empty compilation, and it - // would have updated inProgressProject to remove all the documents. Thus, that is no different than - // the "add" case above. - if (oldState != null) + // Otherwise, we're not finalized, or the document has changed. We'll create an in-progress state + // to represent the new state of the project, with all the necessary translation steps to + // incorporate the new document. + var pendingActions = ImmutableList<(ProjectState, CompilationAndGeneratorDriverTranslationAction)>.Empty; + if (!inProgressProject.DocumentStates.TryGetState(docState.Id, out oldState) || oldState != docState) { - // Scenario 1. The document had been previously parsed and it's there, so we can update it with - // our current state. Note if no compilation existed GetPartialCompilationState would have - // produced an empty one, and removed any documents, so inProgressProject.DocumentStates would - // have been empty originally. - pendingActions = [(inProgressProject, new CompilationAndGeneratorDriverTranslationAction.TouchDocumentAction(oldState, docState))]; - inProgressProject = inProgressProject.UpdateDocument(docState, contentChanged: true); - } - else - { - // We're in either scenario 2 or 3. Do we have an existing document to try replacing? Note: the - // file path here corresponds to Document.FilePath. If a document's file path is null, we then - // substitute Document.Name, so we usually expect there to be a unique string regardless. - oldState = inProgressProject.DocumentStates.States.FirstOrDefault(kvp => kvp.Value.FilePath == docState.FilePath).Value; - if (oldState is null) + // We do not have the exact document. It either means this document was recently added, or the + // document was recently changed. We now need to update both the inProgressState and the + // compilation. There are several possibilities we want to consider: + // + // 1. An earlier version of the document is present in the compilation, and we just need to update it to the current version + // 2. The document wasn't present in the original snapshot at all, and we just need to add the + // document. + // 3. The document wasn't present in the original snapshot, but an older file had been removed that + // had the same file path. As a heuristic, we remove the old one so we don't end up with + // duplicate documents. + // + // Note it's possible that we simply had never tried to produce a compilation yet for this project + // at all, in that case GetPartialCompilationState would have produced an empty compilation, and it + // would have updated inProgressProject to remove all the documents. Thus, that is no different than + // the "add" case above. + if (oldState != null) { - // Scenario 2. - pendingActions = [(inProgressProject, new CompilationAndGeneratorDriverTranslationAction.AddDocumentsAction([docState]))]; - inProgressProject = inProgressProject.AddDocuments([docState]); + // Scenario 1. The document had been previously parsed and it's there, so we can update it with + // our current state. Note if no compilation existed GetPartialCompilationState would have + // produced an empty one, and removed any documents, so inProgressProject.DocumentStates would + // have been empty originally. + pendingActions = [(inProgressProject, new CompilationAndGeneratorDriverTranslationAction.TouchDocumentAction(oldState, docState))]; + inProgressProject = inProgressProject.UpdateDocument(docState, contentChanged: true); } else { - // Scenario 3. The old doc came from some other document with a different ID then we started - // with -- if the document ID still existed we would have been in the Scenario 1 case - // instead. We'll find the old document ID, remove that state, and then add ours. - var inProgressWithDocumentRemoved = inProgressProject.RemoveDocuments([oldState.Id]); - pendingActions = [ - (inProgressProject, new CompilationAndGeneratorDriverTranslationAction.RemoveDocumentsAction([oldState])), + // We're in either scenario 2 or 3. Do we have an existing document to try replacing? Note: the + // file path here corresponds to Document.FilePath. If a document's file path is null, we then + // substitute Document.Name, so we usually expect there to be a unique string regardless. + oldState = inProgressProject.DocumentStates.States.FirstOrDefault(kvp => kvp.Value.FilePath == docState.FilePath).Value; + if (oldState is null) + { + // Scenario 2. + pendingActions = [(inProgressProject, new CompilationAndGeneratorDriverTranslationAction.AddDocumentsAction([docState]))]; + inProgressProject = inProgressProject.AddDocuments([docState]); + } + else + { + // Scenario 3. The old doc came from some other document with a different ID then we started + // with -- if the document ID still existed we would have been in the Scenario 1 case + // instead. We'll find the old document ID, remove that state, and then add ours. + var inProgressWithDocumentRemoved = inProgressProject.RemoveDocuments([oldState.Id]); + pendingActions = [ + (inProgressProject, new CompilationAndGeneratorDriverTranslationAction.RemoveDocumentsAction([oldState])), (inProgressWithDocumentRemoved, new CompilationAndGeneratorDriverTranslationAction.AddDocumentsAction([docState]))]; - inProgressProject = inProgressWithDocumentRemoved - .AddDocuments([docState]); + inProgressProject = inProgressWithDocumentRemoved + .AddDocuments([docState]); + } } } - } - // Transition us to a frozen in progress state. With the best compilations up to this point, and the - // pending actions we'll need to perform on it to get to the final state. - var inProgressState = InProgressState.Create( - isFrozen: true, - compilationPair.CompilationWithoutGeneratedDocuments, - generatorInfo, - compilationPair.CompilationWithGeneratedDocuments, - pendingActions); + // Transition us to a frozen in progress state. With the best compilations up to this point, and the + // pending actions we'll need to perform on it to get to the final state. + var inProgressState = InProgressState.Create( + isFrozen: true, + compilationPair.CompilationWithoutGeneratedDocuments, + generatorInfo, + compilationPair.CompilationWithGeneratedDocuments, + pendingActions); - return new CompilationTracker(inProgressProject, inProgressState, this.SkeletonReferenceCache.Clone()); + return new CompilationTracker(inProgressProject, inProgressState, this.SkeletonReferenceCache.Clone()); + } } /// From a16da4e2a9a0a1e1efd02aa9bc24107b05edb446 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 14 Feb 2024 10:34:49 -0800 Subject: [PATCH 13/13] lint --- .../Solution/SolutionCompilationState.CompilationTracker.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs index fb21484415fea..a245adfbe1d7b 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs @@ -293,7 +293,7 @@ public ICompilationTracker FreezePartialStateWithDocument( var inProgressWithDocumentRemoved = inProgressProject.RemoveDocuments([oldState.Id]); pendingActions = [ (inProgressProject, new CompilationAndGeneratorDriverTranslationAction.RemoveDocumentsAction([oldState])), - (inProgressWithDocumentRemoved, new CompilationAndGeneratorDriverTranslationAction.AddDocumentsAction([docState]))]; + (inProgressWithDocumentRemoved, new CompilationAndGeneratorDriverTranslationAction.AddDocumentsAction([docState]))]; inProgressProject = inProgressWithDocumentRemoved .AddDocuments([docState]); }