Skip to content
Open
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 @@ -47,17 +47,22 @@ public LanguageServerWorkspaceFactory(

// Create the workspace and set analyzer references for it
var workspace = new LanguageServerWorkspace(hostServicesProvider.HostServices, WorkspaceKind.Host);
workspace.SetCurrentSolution(s => s.WithAnalyzerReferences(CreateSolutionLevelAnalyzerReferencesForWorkspace(workspace)), WorkspaceChangeKind.SolutionChanged);
var hostAnalyzerLoaderProvider = workspace.Services.GetRequiredService<IAnalyzerAssemblyLoaderProvider>();
var analyzerReferences = CreateSolutionLevelAnalyzerReferences(hostAnalyzerLoaderProvider);
workspace.SetCurrentSolution(s => s.WithAnalyzerReferences(analyzerReferences), WorkspaceChangeKind.SolutionChanged);

HostProjectFactory = new ProjectSystemProjectFactory(
workspace, fileChangeWatcher, static (_, _) => Task.CompletedTask, _ => { },
CancellationToken.None); // TODO: do we need to introduce a shutdown cancellation token for this?
workspace.ProjectSystemProjectFactory = HostProjectFactory;

// https://github.com/dotnet/roslyn/issues/78560: Move this workspace creation to 'FileBasedProgramsWorkspaceProviderFactory'.
// 'CreateSolutionLevelAnalyzerReferencesForWorkspace' needs to be broken out into its own service for us to be able to move this.
// 'CreateSolutionLevelAnalyzerReferences' needs to be broken out into its own service for us to be able to move this.
var miscellaneousFilesWorkspace = new LanguageServerWorkspace(hostServicesProvider.HostServices, WorkspaceKind.MiscellaneousFiles);
miscellaneousFilesWorkspace.SetCurrentSolution(s => s.WithAnalyzerReferences(CreateSolutionLevelAnalyzerReferencesForWorkspace(miscellaneousFilesWorkspace)), WorkspaceChangeKind.SolutionChanged);
Contract.ThrowIfFalse(
Copy link
Member Author

Choose a reason for hiding this comment

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

This is a basis for the assumption that reusing the same analyzer references array is fine.

Copy link
Member

Choose a reason for hiding this comment

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

It looks likes the AnalyzerFileReference may reference the actual DiagnosticAnalyzer / ISourceGenerator instances? I am not the most familiar here, but at a glance it seems dangerous to share them between two different workspaces?https://github.com/dotnet/roslyn/blob/main/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerFileReference.cs#L39
Maybe @jasonmalinowski is more familiar here?

Copy link
Member Author

Choose a reason for hiding this comment

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

Neither of those types are aware of the workspaces layer, and, are expected to be able to be used with a variety of compilations, etc. in parallel, so, it feels unexpected for the sharing to be problematic. Let's definitely get confirmation though.

Copy link
Member Author

Choose a reason for hiding this comment

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

Note that if we went back to essentially doing this work twice, like before, we would probably add 0.5s to the wall clock time.

Copy link
Member

Choose a reason for hiding this comment

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

yeah if these are shareable, then this definitely makes sense to do. Just not sure on the contract here for that type.

object.ReferenceEquals(hostAnalyzerLoaderProvider, miscellaneousFilesWorkspace.Services.GetRequiredService<IAnalyzerAssemblyLoaderProvider>()),
"Expected same AnalyzerLoaderProvider to be used for both host and misc workspaces.");
miscellaneousFilesWorkspace.SetCurrentSolution(s => s.WithAnalyzerReferences(analyzerReferences), WorkspaceChangeKind.SolutionChanged);

MiscellaneousFilesWorkspaceProjectFactory = new ProjectSystemProjectFactory(
miscellaneousFilesWorkspace, fileChangeWatcher, static (_, _) => Task.CompletedTask, _ => { }, CancellationToken.None);
Expand All @@ -78,29 +83,30 @@ public LanguageServerWorkspaceFactory(
public ProjectSystemHostInfo ProjectSystemHostInfo { get; }
public ProjectTargetFrameworkManager TargetFrameworkManager { get; }

public ImmutableArray<AnalyzerFileReference> CreateSolutionLevelAnalyzerReferencesForWorkspace(Workspace workspace)
private ImmutableArray<AnalyzerFileReference> CreateSolutionLevelAnalyzerReferences(IAnalyzerAssemblyLoaderProvider loaderProvider)
{
var references = ImmutableArray.CreateBuilder<AnalyzerFileReference>();
var loaderProvider = workspace.Services.GetRequiredService<IAnalyzerAssemblyLoaderProvider>();

// Load all analyzers into a fresh shadow copied load context. In the future, if we want to support reloading
// of solution-level analyzer references, we should just need to listen for changes to those analyzer paths and
// then call back into this method to update the solution accordingly.
var analyzerLoader = loaderProvider.CreateNewShadowCopyLoader();
var references = _solutionLevelAnalyzerPaths
.AsParallel()
.Where(analyzerPath =>
{
if (File.Exists(analyzerPath))
return true;

foreach (var analyzerPath in _solutionLevelAnalyzerPaths)
{
if (File.Exists(analyzerPath))
_logger.LogWarning($"Solution-level analyzer at {analyzerPath} could not be found.");
return false;
})
.Select(analyzerPath =>
{
references.Add(new AnalyzerFileReference(analyzerPath, analyzerLoader));
var reference = new AnalyzerFileReference(analyzerPath, analyzerLoader);
_logger.LogDebug($"Solution-level analyzer at {analyzerPath} added to workspace.");
}
else
{
_logger.LogWarning($"Solution-level analyzer at {analyzerPath} could not be found.");
}
}
return reference;
})
.ToImmutableArray();

return references.ToImmutableAndClear();
return references;
}
}
Loading