diff --git a/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis.cs b/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis.cs index b841632..f5e3222 100644 --- a/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis.cs +++ b/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis.cs @@ -42,7 +42,7 @@ using DiagnosticSeverity = Microsoft.CodeAnalysis.DiagnosticSeverity; namespace SharpIDE.Application.Features.Analysis; -public class RoslynAnalysis(ILogger logger, BuildService buildService, AnalyzerFileWatcher analyzerFileWatcher) +public partial class RoslynAnalysis(ILogger logger, BuildService buildService, AnalyzerFileWatcher analyzerFileWatcher) { private readonly ILogger _logger = logger; private readonly BuildService _buildService = buildService; @@ -136,6 +136,12 @@ public class RoslynAnalysis(ILogger logger, BuildService buildSe await _analyzerFileWatcher.StartWatchingFiles(analyzerReferencePaths); _workspace.ClearSolution(); var solution = _workspace.AddSolution(solutionInfo); + + // If these aren't added, IDiagnosticAnalyzerService will not return compiler analyzer diagnostics + // Note that we aren't currently using IDiagnosticAnalyzerService + //var solutionAnalyzerReferences = CreateSolutionLevelAnalyzerReferencesForWorkspace(_workspace); + //solution = solution.WithAnalyzerReferences(solutionAnalyzerReferences); + //_workspace.SetCurrentSolution(solution); } timer.Stop(); _logger.LogInformation("RoslynAnalysis: Solution loaded in {ElapsedMilliseconds}ms", timer.ElapsedMilliseconds); diff --git a/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis_SolutionAnalyzerReferences.cs b/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis_SolutionAnalyzerReferences.cs new file mode 100644 index 0000000..df30d00 --- /dev/null +++ b/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis_SolutionAnalyzerReferences.cs @@ -0,0 +1,43 @@ +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Host; +using Microsoft.Extensions.Logging; +using Roslyn.Utilities; + +namespace SharpIDE.Application.Features.Analysis; + +public partial class RoslynAnalysis +{ + // https://github.com/dotnet/roslyn/blob/a5ff3f7bcb8cb7116709009e7bbafc73ce2d4c79/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerWorkspaceFactory.cs#L81 + public ImmutableArray CreateSolutionLevelAnalyzerReferencesForWorkspace(Workspace workspace) + { + var solutionLevelAnalyzerPaths = new DirectoryInfo(AppContext.BaseDirectory).GetFiles("*.dll") + .Where(f => f.Name.StartsWith("Microsoft.CodeAnalysis.", StringComparison.Ordinal) && !f.Name.Contains("LanguageServer", StringComparison.Ordinal)) + .Select(f => f.FullName) + .ToImmutableArray(); + + var references = ImmutableArray.CreateBuilder(); + var loaderProvider = workspace.Services.GetRequiredService(); + + // 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(); + + foreach (var analyzerPath in solutionLevelAnalyzerPaths) + { + if (File.Exists(analyzerPath)) + { + references.Add(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 references.ToImmutableAndClear(); + } +} diff --git a/tests/SharpIDE.Application.IntegrationTests/Features/Analysis/RoslynAnalysisTests.cs b/tests/SharpIDE.Application.IntegrationTests/Features/Analysis/RoslynAnalysisTests.cs index c578102..a03d9a4 100644 --- a/tests/SharpIDE.Application.IntegrationTests/Features/Analysis/RoslynAnalysisTests.cs +++ b/tests/SharpIDE.Application.IntegrationTests/Features/Analysis/RoslynAnalysisTests.cs @@ -34,7 +34,7 @@ public class RoslynAnalysisTests var roslynAnalysis = new RoslynAnalysis(logger, buildService, analyzerFileWatcher); - var solutionModel = await VsPersistenceMapper.GetSolutionModel(@"C:\Users\Matthew\Documents\Git\SharpIDE\SharpIDE.sln", TestContext.Current.CancellationToken); + var solutionModel = await VsPersistenceMapper.GetSolutionModel(@"C:\Users\Matthew\Documents\Git\SharpIDE\SharpIDE.slnx", TestContext.Current.CancellationToken); var sharpIdeApplicationProject = solutionModel.AllProjects.Single(p => p.Name == "SharpIDE.Application"); var timer = Stopwatch.StartNew();