Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hook up file watchers for analyzer references #74858

Merged
merged 5 commits into from
Aug 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,10 @@ internal sealed partial class ProjectSystemProject
/// <summary>
/// A semaphore taken for all mutation of any mutable field in this type.
/// </summary>
/// <remarks>This is, for now, intentionally pessimistic. There are no doubt ways that we could allow more to run in parallel,
/// but the current tradeoff is for simplicity of code and "obvious correctness" than something that is subtle, fast, and wrong.</remarks>
private readonly SemaphoreSlim _gate = new SemaphoreSlim(initialCount: 1);
/// <remarks>This is, for now, intentionally pessimistic. There are no doubt ways that we could allow more to run in
/// parallel, but the current tradeoff is for simplicity of code and "obvious correctness" than something that is
/// subtle, fast, and wrong.</remarks>
private readonly SemaphoreSlim _gate = new(initialCount: 1);

/// <summary>
/// The number of active batch scopes. If this is zero, we are not batching, non-zero means we are batching.
Expand All @@ -52,13 +53,13 @@ internal sealed partial class ProjectSystemProject
private readonly List<ProjectReference> _projectReferencesAddedInBatch = [];
private readonly List<ProjectReference> _projectReferencesRemovedInBatch = [];

private readonly Dictionary<string, AnalyzerReference> _analyzerPathsToAnalyzers = [];
private readonly List<AnalyzerReference> _analyzersAddedInBatch = [];
private readonly Dictionary<string, AnalyzerFileReference> _analyzerPathsToAnalyzers = [];
private readonly List<AnalyzerFileReference> _analyzersAddedInBatch = [];

/// <summary>
/// The list of <see cref="AnalyzerReference"/>s that will be removed in this batch.
/// </summary>
private readonly List<AnalyzerReference> _analyzersRemovedInBatch = [];
private readonly List<AnalyzerFileReference> _analyzersRemovedInBatch = [];

private readonly List<Func<SolutionChangeAccumulator, ProjectUpdateState, ProjectUpdateState>> _projectPropertyModificationsInBatch = [];

Expand Down Expand Up @@ -653,12 +654,23 @@ await _projectSystemProjectFactory.ApplyBatchChangeToWorkspaceMaybeAsync(useAsyn
newSolution: solutionChanges.Solution.RemoveProjectReference(Id, projectReference));
}

// Analyzer reference removing...
if (_analyzersRemovedInBatch.Count > 0)
{
CyrusNajmabadi marked this conversation as resolved.
Show resolved Hide resolved
projectUpdateState = projectUpdateState.WithIncrementalAnalyzerReferencesRemoved(_analyzersRemovedInBatch);

foreach (var analyzerReference in _analyzersRemovedInBatch)
solutionChanges.UpdateSolutionForProjectAction(Id, solutionChanges.Solution.RemoveAnalyzerReference(Id, analyzerReference));
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doesn't look exactly the same as below as we don't hav ea Solution.RemoveAnalyzerReferences (plural) to go with Solution.AddAnalyzerReferences. And i'm not interested in adding that at this point :)

}

// Analyzer reference adding...
solutionChanges.UpdateSolutionForProjectAction(Id, solutionChanges.Solution.AddAnalyzerReferences(Id, _analyzersAddedInBatch));
if (_analyzersAddedInBatch.Count > 0)
{
projectUpdateState = projectUpdateState.WithIncrementalAnalyzerReferencesAdded(_analyzersAddedInBatch);

// Analyzer reference removing...
foreach (var analyzerReference in _analyzersRemovedInBatch)
solutionChanges.UpdateSolutionForProjectAction(Id, solutionChanges.Solution.RemoveAnalyzerReference(Id, analyzerReference));
solutionChanges.UpdateSolutionForProjectAction(
Id, solutionChanges.Solution.AddAnalyzerReferences(Id, _analyzersAddedInBatch));
}

// Other property modifications...
foreach (var propertyModification in _projectPropertyModificationsInBatch)
Expand Down Expand Up @@ -918,47 +930,54 @@ public void AddAnalyzerReference(string fullPath)
foreach (var mappedFullPath in mappedPaths)
{
if (_analyzerPathsToAnalyzers.ContainsKey(mappedFullPath))
{
throw new ArgumentException($"'{fullPath}' has already been added to this project.", nameof(fullPath));
}
}

foreach (var mappedFullPath in mappedPaths)
if (_activeBatchScopes > 0)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i missed from this point downwards.

{
// Are we adding one we just recently removed? If so, we can just keep using that one, and avoid removing
// it once we apply the batch
var analyzerPendingRemoval = _analyzersRemovedInBatch.FirstOrDefault(a => a.FullPath == mappedFullPath);
if (analyzerPendingRemoval != null)
foreach (var mappedFullPath in mappedPaths)
{
_analyzersRemovedInBatch.Remove(analyzerPendingRemoval);
_analyzerPathsToAnalyzers.Add(mappedFullPath, analyzerPendingRemoval);
}
else
{
// Nope, we actually need to make a new one.
var analyzerReference = new AnalyzerFileReference(mappedFullPath, _analyzerAssemblyLoader);

_analyzerPathsToAnalyzers.Add(mappedFullPath, analyzerReference);

if (_activeBatchScopes > 0)
// Are we adding one we just recently removed? If so, we can just keep using that one, and avoid removing
// it once we apply the batch
var analyzerPendingRemoval = _analyzersRemovedInBatch.FirstOrDefault(a => a.FullPath == mappedFullPath);
if (analyzerPendingRemoval != null)
{
_analyzersAddedInBatch.Add(analyzerReference);
_analyzersRemovedInBatch.Remove(analyzerPendingRemoval);
_analyzerPathsToAnalyzers.Add(mappedFullPath, analyzerPendingRemoval);
}
else
{
_projectSystemProjectFactory.ApplyChangeToWorkspace(w => w.OnAnalyzerReferenceAdded(Id, analyzerReference));
// Nope, we actually need to make a new one.
var analyzerReference = new AnalyzerFileReference(mappedFullPath, _analyzerAssemblyLoader);

_analyzersAddedInBatch.Add(analyzerReference);
_analyzerPathsToAnalyzers.Add(mappedFullPath, analyzerReference);
}
}
}
else
{
_projectSystemProjectFactory.ApplyChangeToWorkspaceWithProjectUpdateState((w, projectUpdateState) =>
{
foreach (var mappedFullPath in mappedPaths)
{
var analyzerReference = new AnalyzerFileReference(mappedFullPath, _analyzerAssemblyLoader);
_analyzerPathsToAnalyzers.Add(mappedFullPath, analyzerReference);
w.OnAnalyzerReferenceAdded(Id, analyzerReference);

projectUpdateState = projectUpdateState.WithIncrementalAnalyzerReferenceAdded(analyzerReference);
}

return projectUpdateState;
});
}
}
}

public void RemoveAnalyzerReference(string fullPath)
{
if (string.IsNullOrEmpty(fullPath))
{
throw new ArgumentException("message", nameof(fullPath));
}

var mappedPaths = GetMappedAnalyzerPaths(fullPath);

Expand All @@ -968,28 +987,38 @@ public void RemoveAnalyzerReference(string fullPath)
foreach (var mappedFullPath in mappedPaths)
{
if (!_analyzerPathsToAnalyzers.ContainsKey(mappedFullPath))
{
throw new ArgumentException($"'{fullPath}' is not an analyzer of this project.", nameof(fullPath));
}
}

foreach (var mappedFullPath in mappedPaths)
if (_activeBatchScopes > 0)
{
var analyzerReference = _analyzerPathsToAnalyzers[mappedFullPath];
foreach (var mappedFullPath in mappedPaths)
{
var analyzerReference = _analyzerPathsToAnalyzers[mappedFullPath];

_analyzerPathsToAnalyzers.Remove(mappedFullPath);
_analyzerPathsToAnalyzers.Remove(mappedFullPath);

if (_activeBatchScopes > 0)
{
// This analyzer may be one we've just added in the same batch; in that case, just don't add it in
// the first place.
if (!_analyzersAddedInBatch.Remove(analyzerReference))
_analyzersRemovedInBatch.Add(analyzerReference);
}
else
}
else
{
_projectSystemProjectFactory.ApplyChangeToWorkspaceWithProjectUpdateState((w, projectUpdateState) =>
{
_projectSystemProjectFactory.ApplyChangeToWorkspace(w => w.OnAnalyzerReferenceRemoved(Id, analyzerReference));
}
foreach (var mappedFullPath in mappedPaths)
{
var analyzerReference = _analyzerPathsToAnalyzers[mappedFullPath];
_analyzerPathsToAnalyzers.Remove(mappedFullPath);

w.OnAnalyzerReferenceRemoved(Id, analyzerReference);
projectUpdateState = projectUpdateState.WithIncrementalAnalyzerReferenceRemoved(analyzerReference);
}

return projectUpdateState;
});
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using Microsoft.CodeAnalysis.Diagnostics;

Expand Down Expand Up @@ -123,9 +124,15 @@ public ProjectUpdateState WithIncrementalMetadataReferenceAdded(PortableExecutab
public ProjectUpdateState WithIncrementalAnalyzerReferenceRemoved(AnalyzerFileReference reference)
=> this with { RemovedAnalyzerReferences = RemovedAnalyzerReferences.Add(reference) };

public ProjectUpdateState WithIncrementalAnalyzerReferencesRemoved(List<AnalyzerFileReference> references)
=> this with { RemovedAnalyzerReferences = RemovedAnalyzerReferences.AddRange(references) };

public ProjectUpdateState WithIncrementalAnalyzerReferenceAdded(AnalyzerFileReference reference)
=> this with { AddedAnalyzerReferences = AddedAnalyzerReferences.Add(reference) };

public ProjectUpdateState WithIncrementalAnalyzerReferencesAdded(List<AnalyzerFileReference> references)
=> this with { AddedAnalyzerReferences = AddedAnalyzerReferences.AddRange(references) };

/// <summary>
/// Returns a new instance with any incremental state that should not be saved between updates cleared.
/// </summary>
Expand Down
Loading