diff --git a/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithStrongNames.txt b/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithStrongNames.txt index 786f72ec8..04e9edbb2 100644 --- a/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithStrongNames.txt +++ b/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithStrongNames.txt @@ -1,7 +1,7 @@ --- ################################ # Assembly references report -# Report date/time: 2025-08-08T09:09:47.9050506Z +# Report date/time: 2025-08-11T14:15:22.4061661Z ################################ # # Generated by Devtility CheckAsmRefs v0.11.0.223 @@ -308,8 +308,16 @@ Assembly: 'SonarLint.VisualStudio.RoslynAnalyzerServer, Version=8.24.0.0, Cultur Relative path: 'SonarLint.VisualStudio.RoslynAnalyzerServer.dll' Referenced assemblies: +- 'Microsoft.CodeAnalysis, Version=3.11.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' +- 'Microsoft.CodeAnalysis.Workspaces, Version=3.11.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' +- 'Microsoft.VisualStudio.LanguageServices, Version=3.11.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' - 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' -# Number of references: 1 +- 'SonarLint.VisualStudio.Core, Version=8.24.0.0, Culture=neutral, PublicKeyToken=c5b62af9de6d7244' +- 'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' +- 'System.Collections.Immutable, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' +- 'System.ComponentModel.Composition, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' +- 'System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' +# Number of references: 9 --- Assembly: 'SonarLint.VisualStudio.SLCore, Version=8.24.0.0, Culture=neutral, PublicKeyToken=c5b62af9de6d7244' diff --git a/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithoutStrongNames.txt b/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithoutStrongNames.txt index 80cd846c6..e2ac01290 100644 --- a/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithoutStrongNames.txt +++ b/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithoutStrongNames.txt @@ -1,7 +1,7 @@ --- ################################ # Assembly references report -# Report date/time: 2025-08-08T09:09:47.9050506Z +# Report date/time: 2025-08-11T14:15:22.4061661Z ################################ # # Generated by Devtility CheckAsmRefs v0.11.0.223 @@ -308,8 +308,16 @@ Assembly: 'SonarLint.VisualStudio.RoslynAnalyzerServer, Version=8.24.0.0, Cultur Relative path: 'SonarLint.VisualStudio.RoslynAnalyzerServer.dll' Referenced assemblies: +- 'Microsoft.CodeAnalysis, Version=3.11.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' +- 'Microsoft.CodeAnalysis.Workspaces, Version=3.11.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' +- 'Microsoft.VisualStudio.LanguageServices, Version=3.11.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' - 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' -# Number of references: 1 +- 'SonarLint.VisualStudio.Core, Version=8.24.0.0, Culture=neutral, PublicKeyToken=null' +- 'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' +- 'System.Collections.Immutable, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' +- 'System.ComponentModel.Composition, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' +- 'System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' +# Number of references: 9 --- Assembly: 'SonarLint.VisualStudio.SLCore, Version=8.24.0.0, Culture=neutral, PublicKeyToken=null' diff --git a/src/RoslynAnalyzerServer.UnitTests/Analysis/DiagnosticDuplicatesComparerTests.cs b/src/RoslynAnalyzerServer.UnitTests/Analysis/DiagnosticDuplicatesComparerTests.cs new file mode 100644 index 000000000..b2c88d676 --- /dev/null +++ b/src/RoslynAnalyzerServer.UnitTests/Analysis/DiagnosticDuplicatesComparerTests.cs @@ -0,0 +1,118 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2025 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis; + +namespace SonarLint.VisualStudio.RoslynAnalyzerServer.UnitTests.Analysis; + +[TestClass] +public class DiagnosticDuplicatesComparerTests +{ + private readonly DiagnosticDuplicatesComparer testSubject = DiagnosticDuplicatesComparer.Instance; + private readonly SonarDiagnostic diagnostic1 = CreateDiagnostic("rule1", "file1.cs", 1, 1, 1, 10); + + [TestMethod] + public void Equals_SameReference_ReturnsTrue() + { + var result = testSubject.Equals(diagnostic1, diagnostic1); + + result.Should().BeTrue(); + } + + [TestMethod] + public void Equals_FirstArgumentNull_ReturnsFalse() + { + var result = testSubject.Equals(null, diagnostic1); + + result.Should().BeFalse(); + } + + [TestMethod] + public void Equals_SecondArgumentNull_ReturnsFalse() + { + var result = testSubject.Equals(diagnostic1, null); + + result.Should().BeFalse(); + } + + [TestMethod] + public void Equals_SameRuleKeyAndLocation_ReturnsTrue() + { + var diagnostic2 = CreateDiagnostic("rule1", "file1.cs", 1, 1, 1, 10); + + var result = testSubject.Equals(diagnostic1, diagnostic2); + + result.Should().BeTrue(); + } + + [TestMethod] + [DataRow("rule2", "file1.cs", 1, 1, 1, 10, DisplayName = "Different RuleKey")] + [DataRow("rule1", "file2.cs", 1, 1, 1, 10, DisplayName = "Different FilePath")] + [DataRow("rule1", "file1.cs", 2, 1, 1, 10, DisplayName = "Different StartLine")] + [DataRow("rule1", "file1.cs", 1, 1, 2, 10, DisplayName = "Different EndLine")] + [DataRow("rule1", "file1.cs", 1, 2, 1, 10, DisplayName = "Different StartLineOffset")] + [DataRow("rule1", "file1.cs", 1, 1, 1, 11, DisplayName = "Different EndLineOffset")] + public void Equals_DifferentValues_ReturnsFalse(string ruleKey, string filePath, int startLine, int startLineOffset, int endLine, int endLineOffset) + { + var diagnostic2 = CreateDiagnostic(ruleKey, filePath, startLine, startLineOffset, endLine, endLineOffset); + + var result = testSubject.Equals(diagnostic1, diagnostic2); + + result.Should().BeFalse(); + } + + [TestMethod] + public void GetHashCode_SameObjects_ReturnsSameHashCode() + { + var diagnostic2 = CreateDiagnostic("rule1", "file1.cs", 1, 1, 1, 10); + + var hash1 = testSubject.GetHashCode(diagnostic1); + var hash2 = testSubject.GetHashCode(diagnostic2); + + hash1.Should().Be(hash2); + } + + [TestMethod] + public void GetHashCode_DifferentObjects_ReturnsDifferentHashCodes() + { + var diagnostic2 = CreateDiagnostic("rule2", "file2.cs", 2, 2, 2, 20); + + var hash1 = testSubject.GetHashCode(diagnostic1); + var hash2 = testSubject.GetHashCode(diagnostic2); + + hash1.Should().NotBe(hash2); + } + + [TestMethod] + public void Instance_ReturnsSingletonInstance() + { + var instance1 = DiagnosticDuplicatesComparer.Instance; + var instance2 = DiagnosticDuplicatesComparer.Instance; + + instance1.Should().BeSameAs(instance2); + } + + private static SonarDiagnostic CreateDiagnostic(string ruleKey, string filePath, int startLine, int startLineOffset, int endLine, int endLineOffset) + { + var textRange = new SonarTextRange(startLine, endLine, startLineOffset, endLineOffset); + var location = new SonarDiagnosticLocation("message", filePath, textRange); + return new SonarDiagnostic(ruleKey, location); + } +} diff --git a/src/RoslynAnalyzerServer.UnitTests/Analysis/SequentialSonarRoslynAnalysisEngineTests.cs b/src/RoslynAnalyzerServer.UnitTests/Analysis/SequentialSonarRoslynAnalysisEngineTests.cs new file mode 100644 index 000000000..dcc65978b --- /dev/null +++ b/src/RoslynAnalyzerServer.UnitTests/Analysis/SequentialSonarRoslynAnalysisEngineTests.cs @@ -0,0 +1,274 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2025 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Text; +using SonarLint.VisualStudio.Core; +using SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis; +using SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis.Wrappers; +using SonarLint.VisualStudio.TestInfrastructure; +using Language = SonarLint.VisualStudio.Core.Language; + +namespace SonarLint.VisualStudio.RoslynAnalyzerServer.UnitTests.Analysis; + +[TestClass] +public class SequentialSonarRoslynAnalysisEngineTests +{ + private IRoslynDiagnosticsConverter diagnosticsConverter = null!; + private ISonarRoslynProjectCompilationProvider projectCompilationProvider = null!; + private TestLogger logger = null!; + private ImmutableDictionary configurations = null!; + private CancellationToken cancellationToken; + private SequentialSonarRoslynAnalysisEngine testSubject = null!; + + [TestInitialize] + public void TestInitialize() + { + diagnosticsConverter = Substitute.For(); + projectCompilationProvider = Substitute.For(); + logger = Substitute.ForPartsOf(); + + testSubject = new SequentialSonarRoslynAnalysisEngine(diagnosticsConverter, projectCompilationProvider, logger); + + configurations = ImmutableDictionary.Create(); + cancellationToken = new CancellationToken(); + } + + [TestMethod] + public void MefCtor_CheckIsExported() => + MefTestHelpers.CheckTypeCanBeImported( + MefTestHelpers.CreateExport(), + MefTestHelpers.CreateExport(), + MefTestHelpers.CreateExport()); + + [TestMethod] + public void MefCtor_CheckIsSingleton() => MefTestHelpers.CheckIsSingletonMefComponent(); + + [TestMethod] + public async Task AnalyzeAsync_EmptyAnalysisCommands_ReturnsEmptyCollection() + { + var result = await testSubject.AnalyzeAsync([], configurations, cancellationToken); + + result.Should().NotBeNull(); + result.Should().BeEmpty(); + } + + [TestMethod] + public async Task AnalyzeAsync_SingleProjectWithNoAnalysisCommands_ReturnsEmptyCollection() + { + var (project, commands, compilation) = SetupProjectAndCommands(); + + var result = await testSubject.AnalyzeAsync([commands], configurations, cancellationToken); + + result.Should().NotBeNull(); + result.Should().BeEmpty(); + await projectCompilationProvider.Received(1).GetProjectCompilationAsync(project, configurations, cancellationToken); + } + + public static object[][] TestData => + [ + [Language.CSharp], + [Language.VBNET] + ]; + + [DataTestMethod] + [DynamicData(nameof(TestData))] + public async Task AnalyzeAsync_SingleProjectWithSingleAnalysisCommand_ReturnsDiagnostics(Language language) + { + var (project, commands, compilation) = SetupProjectAndCommands(); + compilation.Language.Returns(language); + var (command, diagnostic, sonarDiagnostic) = SetupCommandWithDiagnostic( + compilation, + "test-rule", + "test message"); + AddCommandToProject(command, commands); + + var result = await testSubject.AnalyzeAsync([commands], configurations, cancellationToken); + + result.Should().NotBeNull(); + result.Should().ContainSingle(); + result.Single().Should().Be(sonarDiagnostic); + + VerifyAnalysisExecution(project, compilation, command, diagnostic, language); + } + + [TestMethod] + public async Task AnalyzeAsync_DuplicateDiagnostics_ReturnsSingleDiagnostic() + { + var (project, commands, compilation) = SetupProjectAndCommands(); + var (command1, diagnostic1, sonarDiagnostic1) = SetupCommandWithDiagnostic( + compilation, + "test-rule", + "test message"); + AddCommandToProject(command1, commands); + var (command2, diagnostic2, _) = SetupCommandWithDiagnostic( + compilation, + "test-rule-duplicate", + "test message duplicate", + sonarDiagnostic1); + AddCommandToProject(command2, commands); + + var result = await testSubject.AnalyzeAsync([commands], configurations, cancellationToken); + + result.Should().NotBeNull(); + result.Should().ContainSingle(); + result.Single().Should().Be(sonarDiagnostic1); + + VerifyAnalysisExecution(project, compilation, command1, diagnostic1); + VerifyAnalysisExecution(project, compilation, command2, diagnostic2); + + // Verify that a log message is written when a duplicate diagnostic is discarded + logger.AssertPartialOutputStringExists($"Duplicate diagnostic discarded ID: {sonarDiagnostic1.RuleKey}, File: {sonarDiagnostic1.PrimaryLocation.FilePath}, Line: {sonarDiagnostic1.PrimaryLocation.TextRange.StartLine}"); + } + + [TestMethod] + public async Task AnalyzeAsync_MultipleProjects_ProcessesAllProjects() + { + var (project1, commands1, compilation1) = SetupProjectAndCommands(); + var (command1, diagnostic1, sonarDiagnostic1) = SetupCommandWithDiagnostic( + compilation1, + "rule1", + "message1"); + AddCommandToProject(command1, commands1); + var (project2, commands2, compilation2) = SetupProjectAndCommands(); + var (command2, diagnostic2, sonarDiagnostic2) = SetupCommandWithDiagnostic( + compilation2, + "rule2", + "message2"); + AddCommandToProject(command2, commands2); + + var result = await testSubject.AnalyzeAsync([commands1, commands2], configurations, cancellationToken); + + result.Should().BeEquivalentTo([sonarDiagnostic1, sonarDiagnostic2]); + VerifyAnalysisExecution(project1, compilation1, command1, diagnostic1); + VerifyAnalysisExecution(project2, compilation2, command2, diagnostic2); + } + + [TestMethod] + public async Task AnalyzeAsync_SingleProjectWithMultipleCommands_ReturnsAllDiagnostics() + { + var (project, commands, compilation) = SetupProjectAndCommands(); + + var command1 = Substitute.For(); + var diagnostic1A = CreateTestDiagnostic("rule1"); + var diagnostic1B = CreateTestDiagnostic("rule2"); + var sonarDiagnostic1A = CreateSonarDiagnostic("rule1", "message1"); + var sonarDiagnostic1B = CreateSonarDiagnostic("rule2", "message2"); + command1.ExecuteAsync(compilation, cancellationToken).Returns(ImmutableArray.Create(diagnostic1A, diagnostic1B)); + diagnosticsConverter.ConvertToSonarDiagnostic(diagnostic1A, Arg.Any()).Returns(sonarDiagnostic1A); + diagnosticsConverter.ConvertToSonarDiagnostic(diagnostic1B, Arg.Any()).Returns(sonarDiagnostic1B); + AddCommandToProject(command1, commands); + var command2 = Substitute.For(); + var diagnostic2A = CreateTestDiagnostic("rule3"); + var diagnostic2B = CreateTestDiagnostic("rule4"); + var sonarDiagnostic2A = CreateSonarDiagnostic("rule3", "message3"); + var sonarDiagnostic2B = CreateSonarDiagnostic("rule4", "message4"); + command2.ExecuteAsync(compilation, cancellationToken).Returns(ImmutableArray.Create(diagnostic2A, diagnostic2B)); + diagnosticsConverter.ConvertToSonarDiagnostic(diagnostic2A, Arg.Any()).Returns(sonarDiagnostic2A); + diagnosticsConverter.ConvertToSonarDiagnostic(diagnostic2B, Arg.Any()).Returns(sonarDiagnostic2B); + AddCommandToProject(command2, commands); + + var result = await testSubject.AnalyzeAsync([commands], configurations, cancellationToken); + + result.Should().BeEquivalentTo(sonarDiagnostic1A, sonarDiagnostic1B, sonarDiagnostic2A, sonarDiagnostic2B); + await command1.Received(1).ExecuteAsync(compilation, cancellationToken); + await command2.Received(1).ExecuteAsync(compilation, cancellationToken); + diagnosticsConverter.Received(1).ConvertToSonarDiagnostic(diagnostic1A, Arg.Any()); + diagnosticsConverter.Received(1).ConvertToSonarDiagnostic(diagnostic1B, Arg.Any()); + diagnosticsConverter.Received(1).ConvertToSonarDiagnostic(diagnostic2A, Arg.Any()); + diagnosticsConverter.Received(1).ConvertToSonarDiagnostic(diagnostic2B, Arg.Any()); + } + + private (ISonarRoslynProjectWrapper project, SonarRoslynProjectAnalysisSet projectCommand, ISonarRoslynCompilationWithAnalyzersWrapper projectCompilation) SetupProjectAndCommands() + { + var project = Substitute.For(); + var projectCommands = new SonarRoslynProjectAnalysisSet(project, new List()); + var compilation = SetupCompilation(project); + + return (project, projectCommands, compilation); + } + + private static void AddCommandToProject(ISonarRoslynAnalysisCommand command, SonarRoslynProjectAnalysisSet projectSet) => + ((List)projectSet.AnalysisCommands).Add(command); + + private (ISonarRoslynAnalysisCommand command, Diagnostic diagnostic, SonarDiagnostic sonarDiagnostic) SetupCommandWithDiagnostic( + ISonarRoslynCompilationWithAnalyzersWrapper compilationWithAnalyzers, + string ruleId, + string message, + SonarDiagnostic? existingSonarDiagnostic = null) + { + var command = Substitute.For(); + var diagnostic = CreateTestDiagnostic(ruleId); + command.ExecuteAsync(compilationWithAnalyzers, CancellationToken.None) + .Returns(ImmutableArray.Create(diagnostic)); + + var sonarDiagnostic = existingSonarDiagnostic ?? CreateSonarDiagnostic(ruleId, message); + diagnosticsConverter.ConvertToSonarDiagnostic(diagnostic, Arg.Any()).Returns(sonarDiagnostic); + + return (command, diagnostic, sonarDiagnostic); + } + + private ISonarRoslynCompilationWithAnalyzersWrapper SetupCompilation(ISonarRoslynProjectWrapper project) + { + var compilationWithAnalyzers = Substitute.For(); + projectCompilationProvider.GetProjectCompilationAsync(project, configurations, cancellationToken) + .Returns(compilationWithAnalyzers); + return compilationWithAnalyzers; + } + + private void VerifyAnalysisExecution( + ISonarRoslynProjectWrapper project, + ISonarRoslynCompilationWithAnalyzersWrapper compilationWithAnalyzers, + ISonarRoslynAnalysisCommand analysisCommand, + Diagnostic diagnostic, + Language? language = null) + { + projectCompilationProvider.Received(1) + .GetProjectCompilationAsync(project, configurations, cancellationToken); + analysisCommand.Received(1).ExecuteAsync(compilationWithAnalyzers, cancellationToken); + diagnosticsConverter.Received(1).ConvertToSonarDiagnostic(diagnostic, language ?? Arg.Any()); + } + + private static Diagnostic CreateTestDiagnostic(string id) + { + var descriptor = new DiagnosticDescriptor( + id, + "title", + "message", + "category", + DiagnosticSeverity.Warning, + true); + + var location = Location.Create( + "test.cs", + new TextSpan(0, 1), + new LinePositionSpan(new LinePosition(0, 0), new LinePosition(0, 1))); + + return Diagnostic.Create(descriptor, location); + } + + private static SonarDiagnostic CreateSonarDiagnostic(string ruleId, string message) + { + var textRange = new SonarTextRange(1, 1, 0, 1); + var location = new SonarDiagnosticLocation(message, "test.cs", textRange); + return new SonarDiagnostic(ruleId, location); + } +} diff --git a/src/RoslynAnalyzerServer.UnitTests/Analysis/SonarRoslynDiagnosticsConverterTests.cs b/src/RoslynAnalyzerServer.UnitTests/Analysis/SonarRoslynDiagnosticsConverterTests.cs new file mode 100644 index 000000000..df07fb034 --- /dev/null +++ b/src/RoslynAnalyzerServer.UnitTests/Analysis/SonarRoslynDiagnosticsConverterTests.cs @@ -0,0 +1,101 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2025 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Text; +using SonarLint.VisualStudio.Core; +using SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis; +using SonarLint.VisualStudio.TestInfrastructure; + +namespace SonarLint.VisualStudio.RoslynAnalyzerServer.UnitTests.Analysis; + +[TestClass] +public class SonarRoslynDiagnosticsConverterTests +{ + private readonly SonarRoslynDiagnosticsConverter testSubject = new(); + + [TestMethod] + public void MefCtor_CheckExports() => + MefTestHelpers.CheckTypeCanBeImported(); + + [TestMethod] + public void MefCtor_CheckIsSingleton() => MefTestHelpers.CheckIsSingletonMefComponent(); + + public static object[][] TestData => + [ + [Language.CSharp, "S1234", "test message", "c:\\test\\file.cs", 0, 0, 3, 4], + [Language.CSharp, "S1234", "test message", "c:\\test\\file.cs", 1, 1, 3, 4], + [Language.CSharp, "S5678", "multi-line issue", "c:\\test\\file2.cs", 5, 10, 15, 20], + [Language.VBNET, "S1234", "test message", "c:\\test\\file.vb", 0, 0, 3, 4] + ]; + + [DataTestMethod] + [DynamicData(nameof(TestData))] + public void ConvertToSonarDiagnostic_ConvertsDiagnosticCorrectly( + Language language, string ruleId, string message, string filePath, + int startLine, int endLine, int startChar, int endChar) + { + var diagnostic = CreateDiagnostic(ruleId, message, filePath, startLine, endLine, startChar, endChar); + var expectedTextRange = new SonarTextRange( + startLine + 1, // Convert to 1-based + endLine + 1, // Convert to 1-based + startChar, + endChar); + var expectedLocation = new SonarDiagnosticLocation( + message, + filePath, + expectedTextRange); + var expectedRuleId = $"{language.RepoInfo.Key}:{ruleId}"; + var expectedDiagnostic = new SonarDiagnostic( + expectedRuleId, + expectedLocation); + + var result = testSubject.ConvertToSonarDiagnostic(diagnostic, language); + + result.Should().BeEquivalentTo(expectedDiagnostic); + } + + private static Diagnostic CreateDiagnostic(string id, string message, string filePath, int startLine, int endLine, int startChar, int endChar) + { + var descriptor = new DiagnosticDescriptor( + id, + "Any Title", + message, + "Any Category", + default, + default); + + var location = CreateLocation(filePath, startLine, endLine, startChar, endChar); + + return Diagnostic.Create(descriptor, location); + } + + private static Location CreateLocation(string filePath, int startLine, int endLine, int startChar, int endChar) + { + var textSpan = new TextSpan(12, 34); + var syntaxTree = Substitute.For(); + var linePositionSpan = new LinePositionSpan( + new LinePosition(startLine, startChar), + new LinePosition(endLine, endChar)); + syntaxTree.GetMappedLineSpan(textSpan, CancellationToken.None).Returns(new FileLinePositionSpan(filePath, linePositionSpan)); + + return Location.Create(syntaxTree, textSpan); + } +} diff --git a/src/RoslynAnalyzerServer.UnitTests/Analysis/SonarRoslynFileSemanticAnalysisTests.cs b/src/RoslynAnalyzerServer.UnitTests/Analysis/SonarRoslynFileSemanticAnalysisTests.cs new file mode 100644 index 000000000..27a0d91cc --- /dev/null +++ b/src/RoslynAnalyzerServer.UnitTests/Analysis/SonarRoslynFileSemanticAnalysisTests.cs @@ -0,0 +1,106 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2025 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Text; +using NSubstitute.ReturnsExtensions; +using SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis; +using SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis.Wrappers; +using SonarLint.VisualStudio.TestInfrastructure; + +namespace SonarLint.VisualStudio.RoslynAnalyzerServer.UnitTests.Analysis; + +[TestClass] +public class SonarRoslynFileSemanticAnalysisTests +{ + private const string TestFilePath = "c:\\test\\file.cs"; + private ISonarRoslynCompilationWithAnalyzersWrapper compilationWrapper = null!; + private TestLogger testLogger = null!; + private SonarRoslynFileSemanticAnalysis testSubject = null!; + + [TestInitialize] + public void TestInitialize() + { + testLogger = Substitute.ForPartsOf(); + compilationWrapper = Substitute.For(); + + testSubject = new SonarRoslynFileSemanticAnalysis(TestFilePath, testLogger); + } + + [TestMethod] + public void MefCtor() => + testSubject.AnalysisFilePath.Should().Be(TestFilePath); + + [TestMethod] + public async Task ExecuteAsync_SemanticModelIsNull_ReturnsEmptyCollection() + { + compilationWrapper.GetSemanticModel(TestFilePath).ReturnsNull(); + + var result = await testSubject.ExecuteAsync(compilationWrapper, CancellationToken.None); + + result.Should().NotBeNull(); + result.Should().BeEmpty(); + } + + [TestMethod] + public async Task ExecuteAsync_SemanticModelExists_ReturnsAnalyzerDiagnostics() + { + var semanticModel = Substitute.For(); + var expectedDiagnostics = ImmutableArray.Create(CreateTestDiagnostic("id1"), CreateTestDiagnostic("id2"), CreateTestDiagnostic("id3")); + compilationWrapper.GetSemanticModel(TestFilePath).Returns(semanticModel); + compilationWrapper.GetAnalyzerSemanticDiagnosticsAsync(semanticModel, CancellationToken.None) + .Returns(expectedDiagnostics); + + var result = await testSubject.ExecuteAsync(compilationWrapper, CancellationToken.None); + + result.Should().BeEquivalentTo(expectedDiagnostics); + } + + [TestMethod] + public async Task ExecuteAsync_CancellationTokenPassed_UsesTokenForDiagnostics() + { + var semanticModel = Substitute.For(); + var cancellationToken = new CancellationToken(true); + compilationWrapper.GetSemanticModel(TestFilePath).Returns(semanticModel); + + await testSubject.ExecuteAsync(compilationWrapper, cancellationToken); + + await compilationWrapper.Received(1).GetAnalyzerSemanticDiagnosticsAsync(semanticModel, cancellationToken); + } + + private static Diagnostic CreateTestDiagnostic(string id) + { + var descriptor = new DiagnosticDescriptor( + id, + "title", + "message", + "category", + DiagnosticSeverity.Warning, + true); + + var location = Location.Create( + "test.cs", + new TextSpan(0, 1), + new LinePositionSpan(new LinePosition(0, 0), new LinePosition(0, 1))); + + return Diagnostic.Create(descriptor, location); + } +} diff --git a/src/RoslynAnalyzerServer.UnitTests/Analysis/SonarRoslynFileSyntaxAnalysisTests.cs b/src/RoslynAnalyzerServer.UnitTests/Analysis/SonarRoslynFileSyntaxAnalysisTests.cs new file mode 100644 index 000000000..5bd35f40b --- /dev/null +++ b/src/RoslynAnalyzerServer.UnitTests/Analysis/SonarRoslynFileSyntaxAnalysisTests.cs @@ -0,0 +1,107 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2025 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Text; +using NSubstitute.ReturnsExtensions; +using SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis; +using SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis.Wrappers; +using SonarLint.VisualStudio.TestInfrastructure; + +namespace SonarLint.VisualStudio.RoslynAnalyzerServer.UnitTests.Analysis; + +[TestClass] +public class SonarRoslynFileSyntaxAnalysisTests +{ + private const string TestFilePath = "test.cs"; + + private ISonarRoslynCompilationWithAnalyzersWrapper compilationWrapper = null!; + private SyntaxTree syntaxTree = null!; + private TestLogger testLogger = null!; + private SonarRoslynFileSyntaxAnalysis testSubject = null!; + + [TestInitialize] + public void TestInitialize() + { + testLogger = Substitute.ForPartsOf(); + compilationWrapper = Substitute.For(); + syntaxTree = Substitute.For(); + + testSubject = new SonarRoslynFileSyntaxAnalysis(TestFilePath, testLogger); + } + + [TestMethod] + public void Constructor_SetsProperties() => + testSubject.AnalysisFilePath.Should().Be(TestFilePath); + + [TestMethod] + public async Task ExecuteAsync_SyntaxTreeExists_ReturnsSyntaxDiagnostics() + { + var expectedDiagnostics = ImmutableArray.Create(CreateTestDiagnostic("id1"), CreateTestDiagnostic("id2")); + compilationWrapper.GetSyntaxTree(TestFilePath).Returns(syntaxTree); + compilationWrapper.GetAnalyzerSyntaxDiagnosticsAsync(syntaxTree, Arg.Any()).Returns(expectedDiagnostics); + + var result = await testSubject.ExecuteAsync(compilationWrapper, CancellationToken.None); + + result.Should().BeEquivalentTo(expectedDiagnostics); + } + + [TestMethod] + public async Task ExecuteAsync_NoSyntaxTree_ReturnsEmptyArray() + { + compilationWrapper.GetSyntaxTree(TestFilePath).ReturnsNull(); + + var result = await testSubject.ExecuteAsync(compilationWrapper, CancellationToken.None); + + result.Should().BeEmpty(); + } + + [TestMethod] + public async Task ExecuteAsync_CancellationTokenPassed_UsesTokenForDiagnostics() + { + var expectedToken = new CancellationToken(true); + compilationWrapper.GetSyntaxTree(TestFilePath).Returns(syntaxTree); + + await testSubject.ExecuteAsync(compilationWrapper, expectedToken); + + await compilationWrapper.Received(1).GetAnalyzerSyntaxDiagnosticsAsync( + syntaxTree, + Arg.Is(token => token.Equals(expectedToken))); + } + + private static Diagnostic CreateTestDiagnostic(string id) + { + var descriptor = new DiagnosticDescriptor( + id, + "title", + "message", + "category", + DiagnosticSeverity.Warning, + true); + + var location = Location.Create( + "test.cs", + new TextSpan(0, 1), + new LinePositionSpan(new LinePosition(0, 0), new LinePosition(0, 1))); + + return Diagnostic.Create(descriptor, location); + } +} diff --git a/src/RoslynAnalyzerServer.UnitTests/Analysis/SonarRoslynProjectCompilationProviderTests.cs b/src/RoslynAnalyzerServer.UnitTests/Analysis/SonarRoslynProjectCompilationProviderTests.cs new file mode 100644 index 000000000..3624e4393 --- /dev/null +++ b/src/RoslynAnalyzerServer.UnitTests/Analysis/SonarRoslynProjectCompilationProviderTests.cs @@ -0,0 +1,169 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2025 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; +using SonarLint.VisualStudio.Core; +using SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis; +using SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis.Wrappers; +using SonarLint.VisualStudio.TestInfrastructure; + +namespace SonarLint.VisualStudio.RoslynAnalyzerServer.UnitTests.Analysis; + +[TestClass] +public class SonarRoslynProjectCompilationProviderTests +{ + private DiagnosticAnalyzer analyzer1 = null!; + private DiagnosticAnalyzer analyzer2 = null!; + private DiagnosticAnalyzer analyzer3 = null!; + private AnalyzerOptions analyzerOptions = null!; + private ImmutableArray analyzers; + private ISonarRoslynCompilationWrapper compilation = null!; + private CompilationOptions compilationOptions = null!; + private ISonarRoslynCompilationWithAnalyzersWrapper compilationWithAnalyzers = null!; + private ImmutableDictionary configurations = null!; + private ImmutableDictionary diagnosticOptions = null!; + private AdditionalText existingAdditionalFile = null!; + private TestLogger logger = null!; + private ISonarRoslynProjectWrapper project = null!; + private AdditionalText sonarLintXml = null!; + private SonarRoslynProjectCompilationProvider testSubject = null!; + + [TestInitialize] + public void TestInitialize() + { + logger = Substitute.ForPartsOf(); + + project = Substitute.For(); + + compilation = Substitute.For(); + compilationOptions = new CSharpCompilationOptions(OutputKind.ConsoleApplication); + compilation.RoslynCompilationOptions.Returns(compilationOptions); + + compilationWithAnalyzers = Substitute.For(); + + sonarLintXml = Substitute.For(); + sonarLintXml.Path.Returns(@"c:\path\to\SonarLint.xml"); + + existingAdditionalFile = Substitute.For(); + existingAdditionalFile.Path.Returns(@"c:\path\to\existing.txt"); + + analyzerOptions = new AnalyzerOptions(ImmutableArray.Create(existingAdditionalFile)); + project.RoslynAnalyzerOptions.Returns(analyzerOptions); + + analyzer1 = Substitute.For(); + analyzer2 = Substitute.For(); + analyzer3 = Substitute.For(); + analyzers = ImmutableArray.Create(analyzer1, analyzer2, analyzer3); + + diagnosticOptions = ImmutableDictionary.Empty + .Add("SomeId", ReportDiagnostic.Warn); + + var configuration = new SonarRoslynAnalysisConfiguration( + sonarLintXml, + diagnosticOptions, + analyzers); + + configurations = ImmutableDictionary.Empty + .Add(Language.CSharp, configuration); + + compilation.Language.Returns(Language.CSharp); + compilation.WithOptions(Arg.Any()).Returns(compilation); + compilation.WithAnalyzers(Arg.Any>(), Arg.Any()) + .Returns(compilationWithAnalyzers); + project.GetCompilationAsync(Arg.Any()).Returns(compilation); + + testSubject = new SonarRoslynProjectCompilationProvider(logger); + } + + [TestMethod] + public void MefCtor_CheckIsExported() => + MefTestHelpers.CheckTypeCanBeImported( + MefTestHelpers.CreateExport()); + + [TestMethod] + public void MefCtor_CheckIsSingleton() => MefTestHelpers.CheckIsSingletonMefComponent(); + + [TestMethod] + public async Task GetProjectCompilationAsync_ConfiguresCompilationWithCorrectOptions() + { + var result = await testSubject.GetProjectCompilationAsync(project, configurations, CancellationToken.None); + + result.Should().Be(compilationWithAnalyzers); + compilation.Received(1).WithOptions(Arg.Is(options => + options.SpecificDiagnosticOptions == diagnosticOptions)); + compilation.Received(1).WithAnalyzers( + Arg.Is>(analyzersArg => + analyzersArg.SequenceEqual(analyzers, null as IEqualityComparer)), + Arg.Is(options => + options.Options != null + && options.Options.AdditionalFiles.SequenceEqual(ImmutableArray.Create(existingAdditionalFile, sonarLintXml), null as IEqualityComparer) + && options.ConcurrentAnalysis == true + && options.ReportSuppressedDiagnostics == false + && options.LogAnalyzerExecutionTime == false)); + } + + [TestMethod] + public async Task GetProjectCompilationAsync_RemovesExistingSonarLintXml() + { + var existingSonarLintXml = Substitute.For(); + existingSonarLintXml.Path.Returns(@"c:\some\other\path\SonarLint.xml"); + var analyzerOptionsWithSonarLintXml = new AnalyzerOptions( + ImmutableArray.Create(existingAdditionalFile, existingSonarLintXml)); + project.RoslynAnalyzerOptions.Returns(analyzerOptionsWithSonarLintXml); + compilation.WithAnalyzers(Arg.Any>(), Arg.Any()) + .Returns(compilationWithAnalyzers); + + await testSubject.GetProjectCompilationAsync(project, configurations, CancellationToken.None); + + compilation.Received(1).WithAnalyzers( + Arg.Any>(), + Arg.Is(options => + options.Options != null + && options.Options.AdditionalFiles.SequenceEqual(ImmutableArray.Create(existingAdditionalFile, sonarLintXml), null as IEqualityComparer) + && options.ConcurrentAnalysis == true + && options.ReportSuppressedDiagnostics == false + && options.LogAnalyzerExecutionTime == false)); + } + + [TestMethod] + public async Task GetProjectCompilationAsync_AnalyzerException_LogsError() + { + CompilationWithAnalyzersOptions capturedOptions = null!; + compilation.WithAnalyzers( + Arg.Any>(), + Arg.Do(x => capturedOptions = x)) + .Returns(compilationWithAnalyzers); + await testSubject.GetProjectCompilationAsync(project, configurations, CancellationToken.None); + capturedOptions.Should().NotBeNull(); + var exception = new InvalidOperationException("test exception"); + var diagnostic = Diagnostic.Create("TestId", "TestCategory", "TestMessage", DiagnosticSeverity.Warning, DiagnosticSeverity.Warning, true, 1); + + capturedOptions.OnAnalyzerException!(exception, analyzer1, diagnostic); + + logger.AssertPartialOutputStringExists( + "Roslyn Analyzer Exception", + analyzer1.GetType().Name, + "TestId", + "test exception"); + } +} diff --git a/src/RoslynAnalyzerServer.UnitTests/Analysis/SonarRoslynSolutionAnalysisCommandProviderTests.cs b/src/RoslynAnalyzerServer.UnitTests/Analysis/SonarRoslynSolutionAnalysisCommandProviderTests.cs new file mode 100644 index 000000000..64dbd83b9 --- /dev/null +++ b/src/RoslynAnalyzerServer.UnitTests/Analysis/SonarRoslynSolutionAnalysisCommandProviderTests.cs @@ -0,0 +1,200 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2025 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using SonarLint.VisualStudio.Core; +using SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis; +using SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis.Wrappers; +using SonarLint.VisualStudio.TestInfrastructure; + +namespace SonarLint.VisualStudio.RoslynAnalyzerServer.UnitTests.Analysis; + +[TestClass] +public class SonarRoslynSolutionAnalysisCommandProviderTests +{ + private const string File1Cs = "file1.cs"; + private const string File2Cs = "file2.cs"; + private const string File3Cs = "file3.cs"; + private const string File4Cs = "file4.cs"; + private const string AnalyzedFile1Cs = "analyzedFile1.cs"; + private const string AnalyzedFile2Cs = "analyzedFile2.cs"; + private const string AnalyzedFile3Cs = "analyzedFile3.cs"; + private const string Project1 = "project1"; + private const string Project2 = "project2"; + private const string Project3 = "project3"; + private const string Project4 = "project4"; + + private ISonarRoslynWorkspaceWrapper workspaceWrapper = null!; + private TestLogger logger = null!; + private ISonarRoslynSolutionWrapper solutionWrapper = null!; + private SonarRoslynSolutionAnalysisCommandProvider testSubject = null!; + + [TestInitialize] + public void TestInitialize() + { + workspaceWrapper = Substitute.For(); + logger = new TestLogger(); + solutionWrapper = Substitute.For(); + workspaceWrapper.CurrentSolution.Returns(solutionWrapper); + testSubject = new SonarRoslynSolutionAnalysisCommandProvider(workspaceWrapper, logger); + } + + [TestMethod] + public void MefCtor_CheckIsExported() => + MefTestHelpers.CheckTypeCanBeImported( + MefTestHelpers.CreateExport(), + MefTestHelpers.CreateExport()); + + [TestMethod] + public void MefCtor_CheckIsSingleton() => + MefTestHelpers.CheckIsSingletonMefComponent(); + + [TestMethod] + public void GetAnalysisCommandsForCurrentSolution_NoProjects_ReturnsEmptyList() + { + solutionWrapper.Projects.Returns(new List()); + + var result = testSubject.GetAnalysisCommandsForCurrentSolution([File1Cs]); + + result.Should().BeEmpty(); + logger.AssertPartialOutputStringExists("No projects to analyze"); + } + + [TestMethod] + public void GetAnalysisCommandsForCurrentSolution_ProjectDoesNotSupportCompilation_SkipsProject() + { + var project1 = CreateProject(Project1, false); + solutionWrapper.Projects.Returns([project1]); + + var result = testSubject.GetAnalysisCommandsForCurrentSolution([File1Cs]); + + result.Should().BeEmpty(); + logger.AssertPartialOutputStringExists("Failed to get compilation for project: project1"); + logger.AssertPartialOutputStringExists("No projects to analyze"); + } + + [TestMethod] + public void GetAnalysisCommandsForCurrentSolution_ProjectWithNoMatchingFiles_SkipsProject() + { + var project1 = CreateProject(Project1); + project1.ContainsDocument(Arg.Any(), out _).Returns(false); + + solutionWrapper.Projects.Returns([project1]); + + var result = testSubject.GetAnalysisCommandsForCurrentSolution([File1Cs]); + + result.Should().BeEmpty(); + logger.AssertPartialOutputStringExists("No files to analyze in project project1"); + logger.AssertPartialOutputStringExists("No projects to analyze"); + } + + [TestMethod] + public void GetAnalysisCommandsForCurrentSolution_MultipleProjects_OnlyProjectWithFilesIsReturned() + { + var project1 = CreateProject(Project1); + project1.ContainsDocument(Arg.Any(), out _).Returns(false); + var project2 = CreateProject(Project2); + SetupContainsDocument(project2, File1Cs, AnalyzedFile1Cs); + solutionWrapper.Projects.Returns([project1, project2]); + + var result = testSubject.GetAnalysisCommandsForCurrentSolution([File1Cs]); + + result.Should().ContainSingle(); + var command = result.Single(); + command.Project.Should().Be(project2); + command.AnalysisCommands.OfType().Should().HaveCount(1); + command.AnalysisCommands.OfType().Should().HaveCount(1); + logger.AssertPartialOutputStringExists("No files to analyze in project project1"); + } + + [TestMethod] + public void GetAnalysisCommandsForCurrentSolution_MultipleMatchingFiles_AllFilesIncluded() + { + var project = CreateProject(Project1); + SetupContainsDocument(project, File1Cs, AnalyzedFile1Cs); + SetupContainsDocument(project, File2Cs, AnalyzedFile2Cs); + project.ContainsDocument(File3Cs, out _).Returns(false); + solutionWrapper.Projects.Returns([project]); + + var result = testSubject.GetAnalysisCommandsForCurrentSolution([File1Cs, File2Cs, File3Cs]); + + var command = result.Single(); + command.Project.Should().Be(project); + command.AnalysisCommands.Should().HaveCount(4); + ValidateContainsAllTypesOfAnalysisForFile(command, AnalyzedFile1Cs); + ValidateContainsAllTypesOfAnalysisForFile(command, AnalyzedFile2Cs); + } + + [TestMethod] + public void GetAnalysisCommandsForCurrentSolution_MixedProjectResults_ReturnsCorrectProjects() + { + var project1 = CreateProject(Project1, false); + var project2 = CreateProject(Project2); + project2.ContainsDocument(Arg.Any(), out _).Returns(false); + var project3 = CreateProject(Project3); + SetupContainsDocument(project3, File1Cs, AnalyzedFile1Cs); + SetupContainsDocument(project3, File2Cs, AnalyzedFile2Cs); + var project4 = CreateProject(Project4); + SetupContainsDocument(project4, File3Cs, AnalyzedFile3Cs); + SetupContainsDocument(project4, File1Cs, AnalyzedFile1Cs); + solutionWrapper.Projects.Returns([project1, project2, project3, project4]); + + var result = testSubject.GetAnalysisCommandsForCurrentSolution([File1Cs, File2Cs, File3Cs, File4Cs]); + + result.Should().HaveCount(2); + result[0].Project.Should().Be(project3); + result[0].AnalysisCommands.Should().HaveCount(4); + ValidateContainsAllTypesOfAnalysisForFile(result[0], AnalyzedFile1Cs); + ValidateContainsAllTypesOfAnalysisForFile(result[0], AnalyzedFile2Cs); + result[1].Project.Should().Be(project4); + result[1].AnalysisCommands.Should().HaveCount(4); + ValidateContainsAllTypesOfAnalysisForFile(result[1], AnalyzedFile3Cs); + ValidateContainsAllTypesOfAnalysisForFile(result[1], AnalyzedFile1Cs); + logger.AssertPartialOutputStringExists("Failed to get compilation for project: project1"); + logger.AssertPartialOutputStringExists("No files to analyze in project project2"); + } + + private void ValidateContainsAllTypesOfAnalysisForFile(SonarRoslynProjectAnalysisSet set, string analysisFilePath) + { + ValidateContainsSyntacticAnalysisForFile(set, analysisFilePath); + ValidateContainsSemanticAnalysisForFile(set, analysisFilePath); + } + + private void ValidateContainsSyntacticAnalysisForFile(SonarRoslynProjectAnalysisSet set, string analysisFilePath) => + set.AnalysisCommands.Any(x => x is SonarRoslynFileSyntaxAnalysis semanticAnalysis && semanticAnalysis.AnalysisFilePath == analysisFilePath).Should().BeTrue(); + + private void ValidateContainsSemanticAnalysisForFile(SonarRoslynProjectAnalysisSet set, string analysisFilePath) => + set.AnalysisCommands.Any(x => x is SonarRoslynFileSemanticAnalysis semanticAnalysis && semanticAnalysis.AnalysisFilePath == analysisFilePath).Should().BeTrue(); + + + private static ISonarRoslynProjectWrapper CreateProject(string projectName, bool supportsCompilation = true) + { + var project = Substitute.For(); + project.Name.Returns(projectName); + project.SupportsCompilation.Returns(supportsCompilation); + return project; + } + + private static void SetupContainsDocument(ISonarRoslynProjectWrapper project, string file, string analyzedFile) => + project.ContainsDocument(file, out Arg.Any()).Returns(x => + { + x[1] = analyzedFile; + return true; + }); +} diff --git a/src/RoslynAnalyzerServer.UnitTests/Analysis/Wrappers/SonarRoslynWorkspaceWrapperTests.cs b/src/RoslynAnalyzerServer.UnitTests/Analysis/Wrappers/SonarRoslynWorkspaceWrapperTests.cs new file mode 100644 index 000000000..57c2a1fc5 --- /dev/null +++ b/src/RoslynAnalyzerServer.UnitTests/Analysis/Wrappers/SonarRoslynWorkspaceWrapperTests.cs @@ -0,0 +1,32 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2025 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis.Wrappers; +using SonarLint.VisualStudio.TestInfrastructure; + +namespace SonarLint.VisualStudio.RoslynAnalyzerServer.UnitTests.Analysis.Wrappers; + +[TestClass] +public class SonarRoslynWorkspaceWrapperTests +{ + [TestMethod] + public void MefCtor_CheckIsSingleton() => + MefTestHelpers.CheckIsSingletonMefComponent(); +} diff --git a/src/RoslynAnalyzerServer.UnitTests/RoslynAnalyzerServer.UnitTests.csproj b/src/RoslynAnalyzerServer.UnitTests/RoslynAnalyzerServer.UnitTests.csproj index e74cffb04..565b4d9b4 100644 --- a/src/RoslynAnalyzerServer.UnitTests/RoslynAnalyzerServer.UnitTests.csproj +++ b/src/RoslynAnalyzerServer.UnitTests/RoslynAnalyzerServer.UnitTests.csproj @@ -6,10 +6,17 @@ SonarLint.VisualStudio.RoslynAnalyzerServer.UnitTests SonarLint.VisualStudio.RoslynAnalyzerServer.UnitTests enable + enable + + + + + + diff --git a/src/RoslynAnalyzerServer.UnitTests/packages.lock.json b/src/RoslynAnalyzerServer.UnitTests/packages.lock.json index 4e6a6dffb..4d06016b5 100644 --- a/src/RoslynAnalyzerServer.UnitTests/packages.lock.json +++ b/src/RoslynAnalyzerServer.UnitTests/packages.lock.json @@ -14,6 +14,30 @@ "resolved": "0.11.4", "contentHash": "zSCkwOgc5OyfMfEeMr9x0K7WCDf8i6VdF2RtCLN/4m6iebTtJQdeoJ9IS4/RyYHuLUYjrm0sd+siWbaSvSzRYQ==" }, + "Microsoft.CodeAnalysis.Common": { + "type": "Direct", + "requested": "[3.11.0, )", + "resolved": "3.11.0", + "contentHash": "FDKSkRRXnaEWMa2ONkLMo0ZAt/uiV1XIXyodwKIgP1AMIKA7JJKXx/OwFVsvkkUT4BeobLwokoxFw70fICahNg==", + "dependencies": { + "Microsoft.CodeAnalysis.Analyzers": "3.3.2", + "System.Collections.Immutable": "5.0.0", + "System.Memory": "4.5.4", + "System.Reflection.Metadata": "5.0.0", + "System.Runtime.CompilerServices.Unsafe": "5.0.0", + "System.Text.Encoding.CodePages": "4.5.1", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.CodeAnalysis.CSharp": { + "type": "Direct", + "requested": "[3.11.0, )", + "resolved": "3.11.0", + "contentHash": "aDRRb7y/sXoJyDqFEQ3Il9jZxyUMHkShzZeCRjQf3SS84n2J0cTEi3TbwVZE9XJvAeMJhGfVVxwOdjYBg6ljmw==", + "dependencies": { + "Microsoft.CodeAnalysis.Common": "[3.11.0]" + } + }, "Microsoft.NET.Test.Sdk": { "type": "Direct", "requested": "[16.6.1, )", @@ -71,16 +95,887 @@ "resolved": "5.1.1", "contentHash": "rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==" }, + "envdte": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "AC6jYeSnDnYZEs5nHKEtBupRWAQxriX2X3M25HyJlU9cvCCqPCByMwIbvlz8kXk+GfGxSL8sN+YOihU2SvrjXw==", + "dependencies": { + "Microsoft.VisualStudio.Interop": "17.0.31902.203" + } + }, + "envdte100": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "XICXfPHF4SQzqpUtQgXZsuSTyYOdSOymPMUqH/Q6QBrpEoMiJxmW/eEvLwgqJqiW5+HaajJImlVJkrnZJ4fjfg==", + "dependencies": { + "Microsoft.VisualStudio.Interop": "17.0.31902.203" + } + }, + "envdte80": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "79xAQpQmKqNnBWtH96Fm+onXsCS7ZTK0CfhCIe3BC56whCMwyh52M/+Qj/xOGhbFnPpjEzJhPnoL1jppniyRAw==", + "dependencies": { + "Microsoft.VisualStudio.Interop": "17.0.31902.203" + } + }, + "envdte90": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "AMFd0yjzXUV26i0Yzr5MBdolXtz92NZWUvacohGRFFHIHaa8BkKl1c40nw9m33l4cXcwMECsvPkhAcfietYkQg==", + "dependencies": { + "Microsoft.VisualStudio.Interop": "17.0.31902.203" + } + }, + "envdte90a": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "8rZmZEBu8uDMF1Fq1CITa540cJA8lVj9d9B2D2IgDL6heGkBfQc1nBf2BRcJT81SS9c0wEI1VLIVe3WSPeYG5g==", + "dependencies": { + "Microsoft.VisualStudio.Interop": "17.0.31902.203" + } + }, + "Google.Protobuf": { + "type": "Transitive", + "resolved": "3.6.1", + "contentHash": "741fGeDQjixBJaU2j+0CbrmZXsNJkTn/hWbOh4fLVXndHsCclJmWznCPWrJmPoZKvajBvAz3e8ECJOUvRtwjNQ==" + }, + "Grpc.Tools": { + "type": "Transitive", + "resolved": "1.4.1", + "contentHash": "D5AcNr0yPFz5dqftJYKnMtwg6AEMUics+UysxTXKVuZtresqWUcHIrnscM+KsAIreG7wvdumWzjdIXRIMekCLg==" + }, + "MessagePack": { + "type": "Transitive", + "resolved": "2.2.85", + "contentHash": "3SqAgwNV5LOf+ZapHmjQMUc7WDy/1ur9CfFNjgnfMZKCB5CxkVVbyHa06fObjGTEHZI7mcDathYjkI+ncr92ZQ==", + "dependencies": { + "MessagePack.Annotations": "2.2.85", + "Microsoft.Bcl.AsyncInterfaces": "1.0.0", + "System.Collections.Immutable": "1.5.0", + "System.Memory": "4.5.3", + "System.Reflection.Emit": "4.6.0", + "System.Reflection.Emit.Lightweight": "4.6.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.2", + "System.Threading.Tasks.Extensions": "4.5.3" + } + }, + "MessagePack.Annotations": { + "type": "Transitive", + "resolved": "2.2.85", + "contentHash": "YptRsDCQK35K5FhmZ0LojW4t8I6DpetLfK5KG8PVY2f6h7/gdyr8f4++xdSEK/xS6XX7/GPvEpqszKVPksCsiQ==" + }, + "Microsoft.Alm.Authentication": { + "type": "Transitive", + "resolved": "4.0.0.1", + "contentHash": "G+EwnnZeoee3McrWZQ0TOtQShJyCzFwhJdEXIbz5t+G2e67KICipAHfflDSYwokB43YK+y5J7ArnRHDLYtNliA==", + "dependencies": { + "Microsoft.IdentityModel.Clients.ActiveDirectory": "3.13.8" + } + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.Build.Framework": { + "type": "Transitive", + "resolved": "16.5.0", + "contentHash": "K0hfdWy+0p8DJXxzpNc4T5zHm4hf9QONAvyzvw3utKExmxRBShtV/+uHVYTblZWk+rIHNEHeglyXMmqfSshdFA==" + }, + "Microsoft.CodeAnalysis.Analyzers": { + "type": "Transitive", + "resolved": "3.3.2", + "contentHash": "7xt6zTlIEizUgEsYAIgm37EbdkiMmr6fP6J9pDoKEpiGM4pi32BCPGr/IczmSJI9Zzp0a6HOzpr9OvpMP+2veA==" + }, "Microsoft.CodeCoverage": { "type": "Transitive", "resolved": "16.6.1", "contentHash": "nBYXDgAZCfjsOVzlhMB5olGvX4dTDWB/gWaYS/MhgXBcCz8XJuVGqkfK8LmwlBR/eeUPE9Q/NFZNwlJyMZf0vg==" }, + "Microsoft.CSharp": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + }, + "Microsoft.IdentityModel.Clients.ActiveDirectory": { + "type": "Transitive", + "resolved": "3.13.8", + "contentHash": "RAYmpEVVnl8qWdPmm62k9wm6MUB9H4Gk3l7uBADDCCQJDOkpTAINzgWHTBF8hkaTHFLaGIfgOeeLcKT+K1PG7A==" + }, + "Microsoft.NETCore.Platforms": { + "type": "Transitive", + "resolved": "1.1.1", + "contentHash": "TMBuzAHpTenGbGgk0SMTwyEkyijY/Eae4ZGsFNYJvAr/LDn1ku3Etp3FPxChmDp5HHF3kzJuoaa08N0xjqAJfQ==" + }, + "Microsoft.NETCore.Targets": { + "type": "Transitive", + "resolved": "1.1.3", + "contentHash": "3Wrmi0kJDzClwAC+iBdUBpEKmEle8FQNsCs77fkiOIw/9oYA07bL1EZNX0kQ2OMN3xpwvl0vAtOCYY3ndDNlhQ==" + }, + "Microsoft.ServiceHub.Analyzers": { + "type": "Transitive", + "resolved": "3.0.3078", + "contentHash": "LQsmEP/5i9PvM6O1dx69Yj3C0z/tSWiaLjoX31jQ+ilJZ8x7yqthYOnWaQpeZKxJn+oFxymzGtXgPasnqYM/ww==" + }, + "Microsoft.ServiceHub.Client": { + "type": "Transitive", + "resolved": "3.0.3078", + "contentHash": "hYqQlgUhnTq7VHYfIBvuWCwAiTjqhCfEX7d/ISVtEGEv7/N89QAbL+0XCz2NZRN6yMDtVMEoee5Q4k6/uwWlJg==", + "dependencies": { + "Microsoft.ServiceHub.Framework": "3.0.3078", + "Microsoft.ServiceHub.Resources": "3.0.3078", + "Microsoft.VisualStudio.RemoteControl": "16.3.32", + "Microsoft.VisualStudio.Telemetry": "16.3.176", + "StreamJsonRpc": "2.7.70", + "System.Collections.Immutable": "5.0.0" + } + }, + "Microsoft.ServiceHub.Framework": { + "type": "Transitive", + "resolved": "3.0.3078", + "contentHash": "RMBx+TEE3Fl6CRd1d1ZWKnNPRbPL23NFydDEEjRtZdwTSWe1x0gkUqnGU/ZgtqSsgWUfaQtEPxd8S9qfPGkz0Q==", + "dependencies": { + "Microsoft.ServiceHub.Analyzers": "3.0.3078", + "StreamJsonRpc": "2.7.70", + "System.Collections.Immutable": "5.0.0" + } + }, + "Microsoft.ServiceHub.Resources": { + "type": "Transitive", + "resolved": "3.0.3078", + "contentHash": "02mGIKyVfnXFEeicpV2RbZapHd6vcefFSSZvjAA+O0kWgB9x2D5Pd3M94Il9LiLgFnw3mmxtf68tbEjOhQ0rWg==" + }, + "Microsoft.VisualStudio.CommandBars": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "+eBuWROC04s6NyVhJW/GJIqy8gOhFaxkWiHtuEf2bVTGkKQ/gx/Rjfegj79H/xEPnBmvbKp8e5+dxJ1WA4r1XA==", + "dependencies": { + "Microsoft.VisualStudio.Interop": "17.0.31902.203" + } + }, + "Microsoft.VisualStudio.ComponentModelHost": { + "type": "Transitive", + "resolved": "17.0.487", + "contentHash": "mIvfzFYYtPbf+aZRLjtOmMfksKPGML2U2z7RqHJ+m0AVTumssJbOpaD5gOgYcUvE6+AjFoUpg+NLVjwxdZnVYQ==", + "dependencies": { + "Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime": "17.0.31723.112", + "Microsoft.VisualStudio.Interop": "17.0.31723.112", + "System.ComponentModel.Composition": "4.5.0" + } + }, + "Microsoft.VisualStudio.CoreUtility": { + "type": "Transitive", + "resolved": "17.0.487", + "contentHash": "qk4BeMGWIklu4ia8zf6JPLUGXuJcbjhqfYhIHYbDKETmGxRHoNsvzGBIZT8GkS4VSjcibOFnbCowJfNWy5TfhQ==", + "dependencies": { + "Microsoft.VisualStudio.Threading": "17.0.63", + "System.Collections.Immutable": "5.0.0", + "System.ComponentModel.Composition": "4.5.0" + } + }, + "Microsoft.VisualStudio.Debugger.Interop.10.0": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "YipUF2uyw4MH3WFZf+/4lC9jcHjkL8GgPdwvH800x+kVHAAVIEXJTqPTmsJnBrT/QqXdPHQrM6Srae0VQjG3gA==", + "dependencies": { + "Microsoft.VisualStudio.Debugger.InteropA": "17.0.31902.203" + } + }, + "Microsoft.VisualStudio.Debugger.Interop.11.0": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "NENWpbG7TvdF1TwesraECA+PgprCLR3Mfts7t8fL5KGc59QiRCeZTpVWBzotvRLMQ0lhA0VYYh1ErDV9D+LM2g==", + "dependencies": { + "Microsoft.VisualStudio.Debugger.InteropA": "17.0.31902.203" + } + }, + "Microsoft.VisualStudio.Debugger.Interop.12.0": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "Os4TTwz9mrtS5AzTKMyEr2NPIJmmBGlf+Cn4eFPn6t4yMfDYfaFRnOtDgpiF7IyduB+mywO8v+wMLWiQuJth9Q==", + "dependencies": { + "Microsoft.VisualStudio.Debugger.InteropA": "17.0.31902.203" + } + }, + "Microsoft.VisualStudio.Debugger.Interop.14.0": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "UwQukQ2/HdIxKqHGDQ8oipxrJ4IWLdQfBBuY8Yl61SAVhcGzDP4Z/XkB7RSIbJOAeFDzu/kuKZ3zXR7jXazAJA==", + "dependencies": { + "Microsoft.VisualStudio.Debugger.Interop.11.0": "17.0.31902.203" + } + }, + "Microsoft.VisualStudio.Debugger.Interop.15.0": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "P7C071a0dx7TP9NEBmt8pR3BZMOIqQYNWYW1pvxaY6jOb99/q7sm/KgYDKGrK/HEBefbn6g9T6hrVlUeP2GsWQ==", + "dependencies": { + "Microsoft.VisualStudio.Debugger.Interop.10.0": "17.0.31902.203" + } + }, + "Microsoft.VisualStudio.Debugger.Interop.16.0": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "eYE5pOBbWacuf0pUyQHJH9LuP15x67CUy8VJv9AeJM3E91Y95Ff5hkCasBZtf9TJ9uMVnx0+6PdZLkZdYcS4Yg==", + "dependencies": { + "Microsoft.VisualStudio.Debugger.Interop.10.0": "17.0.31902.203", + "Microsoft.VisualStudio.Debugger.Interop.11.0": "17.0.31902.203" + } + }, + "Microsoft.VisualStudio.Debugger.InteropA": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "y1CLio4/zI5PYWt760mJhduq/gpNZNrDwX2c2kBB5p+D8a1ExH4d2tNDTqbQdciOiNR47YenAcSUpyfHv9pNkg==", + "dependencies": { + "Microsoft.VisualStudio.Interop": "17.0.31902.203" + } + }, + "Microsoft.VisualStudio.Designer.Interfaces": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "eeNZCUX6RJGnc6YC6Bfs8qbEVt7WxWRPXEbEBjyOqCDU4dJPkR1OLVWMUHkA4XM3sXKImv1LmAwHZk3cWK5iGQ==", + "dependencies": { + "Microsoft.VisualStudio.Interop": "17.0.31902.203" + } + }, + "Microsoft.VisualStudio.Editor": { + "type": "Transitive", + "resolved": "17.0.487", + "contentHash": "UuIMD8xLAkvwuszD7jlc4ADRckr59eyJ7YJFepTtja2IDMzt5SAGa0kxxjUQ3Zeg0KuhuwcZBwuYmSCgx5YSjA==", + "dependencies": { + "Microsoft.VisualStudio.CoreUtility": "17.0.487", + "Microsoft.VisualStudio.GraphModel": "17.0.31723.112", + "Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime": "17.0.31723.112", + "Microsoft.VisualStudio.Interop": "17.0.31723.112", + "Microsoft.VisualStudio.Language": "17.0.487", + "Microsoft.VisualStudio.ProjectAggregator": "17.0.31723.112", + "Microsoft.VisualStudio.RpcContracts": "17.0.51", + "Microsoft.VisualStudio.Shell.15.0": "17.0.31723.112", + "Microsoft.VisualStudio.Text.Data": "17.0.487", + "Microsoft.VisualStudio.Text.Logic": "17.0.487", + "Microsoft.VisualStudio.Text.UI": "17.0.487", + "Microsoft.VisualStudio.Text.UI.Wpf": "17.0.487", + "Microsoft.VisualStudio.Threading": "17.0.63", + "Microsoft.VisualStudio.Validation": "17.0.28" + } + }, + "Microsoft.VisualStudio.GraphModel": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "53cv+WBrXiX3Bomk2W+gz74tFqFa54OvJ++u4RYAdntvXb/hx+m1EhBgtHKOFu057rVxAq/YNFY7vLCUusQGoQ==", + "dependencies": { + "Microsoft.VisualStudio.Interop": "17.0.31902.203", + "System.ComponentModel.Composition": "4.5.0" + } + }, + "Microsoft.VisualStudio.ImageCatalog": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "nNfMuMiuC0UX/r5ryjIuqWyj/Wzrz63wuEcX70NtICUNmm/Bc5blXmgH7Et+ArVzZlXi2ExNqbLAi60IpNdaYw==", + "dependencies": { + "Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime": "17.0.31902.203", + "Microsoft.VisualStudio.Interop": "17.0.31902.203" + } + }, + "Microsoft.VisualStudio.Imaging": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "NCJX5aUZxcj32Al5E9PI2XONEjR8gQyzjGL2NfNxw8PzDPd6yMmU96y7rnvv0dclPUw0gCcVMtLnNwpCaeXBdg==", + "dependencies": { + "Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime": "17.0.31902.203", + "Microsoft.VisualStudio.Threading": "17.0.64", + "Microsoft.VisualStudio.Utilities": "17.0.31902.203", + "System.Collections.Immutable": "5.0.0" + } + }, + "Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "d9I8RzOeF9z1L/QJ3nWOMvJphsxe31cU7WBZW7aM0VTWZUWcYwYQH0CZWFPZsXgFWWRgiK5WL7FiMnYJfeDutw==" + }, + "Microsoft.VisualStudio.Interop": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "KWBjmU85KpWJiqRgkvuJfQgW8/1RtkFnJ4e4EIcoXYHab/1tinXv219midxHJoc6Sa6E/7Uo3Ku4bWBQMFE9dA==" + }, + "Microsoft.VisualStudio.Language": { + "type": "Transitive", + "resolved": "17.0.487", + "contentHash": "j8hL3Wwst8hwstbBzY24WV0PWYkcWDAC/Y2JHdO9jdlBjvHCHUPN4zNtu6A9oCTNsyLMxzK+pq+X45FFVTeqrA==", + "dependencies": { + "Microsoft.VisualStudio.CoreUtility": "17.0.487", + "Microsoft.VisualStudio.Text.Data": "17.0.487", + "Microsoft.VisualStudio.Text.Logic": "17.0.487", + "Microsoft.VisualStudio.Text.UI": "17.0.487", + "Newtonsoft.Json": "13.0.1", + "StreamJsonRpc": "2.8.28", + "System.Collections.Immutable": "5.0.0", + "System.ComponentModel.Composition": "4.5.0", + "System.Private.Uri": "4.3.2" + } + }, + "Microsoft.VisualStudio.Language.Intellisense": { + "type": "Transitive", + "resolved": "17.0.487", + "contentHash": "iPi01Ep2YM4rgL6z26s4eEy8hOYwt5jE1aLebknCnBzUAB3avrSVa1AcOo726SdVN8h/h0lVRE4LMrC4WAQ4rQ==", + "dependencies": { + "Microsoft.VisualStudio.CoreUtility": "17.0.487", + "Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime": "17.0.31723.112", + "Microsoft.VisualStudio.Language": "17.0.487", + "Microsoft.VisualStudio.Text.Data": "17.0.487", + "Microsoft.VisualStudio.Text.Logic": "17.0.487", + "Microsoft.VisualStudio.Text.UI": "17.0.487", + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, + "Microsoft.VisualStudio.Language.NavigateTo.Interfaces": { + "type": "Transitive", + "resolved": "17.0.487", + "contentHash": "5/3AstEbNcaZtwwONLcHXc2Nzels7j89jzXsYsxPQCSW9inbDbQoBC6+wtsAwKxOu1BeKpPaGi7bmc7Y+R+vSA==", + "dependencies": { + "Microsoft.VisualStudio.Imaging": "17.0.31723.112", + "Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime": "17.0.31723.112", + "Microsoft.VisualStudio.Interop": "17.0.31723.112", + "Microsoft.VisualStudio.Text.Logic": "17.0.448", + "Microsoft.VisualStudio.Utilities": "17.0.31723.112" + } + }, + "Microsoft.VisualStudio.Language.StandardClassification": { + "type": "Transitive", + "resolved": "17.0.487", + "contentHash": "n7l7tLV69+FtdH+/SPR8MdVc3UsAvlrgE38mMlXtW3dkxx5BYnSiHl93vZ4omDbX45Rzu9b4DDR1HMzg3GaKzQ==", + "dependencies": { + "Microsoft.VisualStudio.Text.Logic": "17.0.487" + } + }, + "Microsoft.VisualStudio.LanguageServer.Client": { + "type": "Transitive", + "resolved": "17.0.5158", + "contentHash": "cZNQHkvjpCnEnxZrLBflAAXc8CwJYMUgDEcCTgrY86Zpa6tHfaHpMoK3aBXtBlzlJ25vjeqBoilXbbCrPrHnJg==", + "dependencies": { + "Microsoft.VisualStudio.CoreUtility": "17.0.448", + "Microsoft.VisualStudio.Shell.15.0": "17.0.31723.112", + "Microsoft.VisualStudio.Utilities": "17.0.31723.112", + "Microsoft.VisualStudio.Validation": "17.0.28", + "StreamJsonRpc": "2.8.28" + } + }, + "Microsoft.VisualStudio.OLE.Interop": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "lhZX3Vxm+bRP7/lb8zYG2DlLx3Ea1tnatV+SUYAJ+sL8Qr7v8VBmZurPxGsKYh5Q7ePNjOlkWk2eSaK6TaxUyg==", + "dependencies": { + "Microsoft.VisualStudio.Interop": "17.0.31902.203" + } + }, + "Microsoft.VisualStudio.Package.LanguageService.15.0": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "ZR8l9shqIHXGzfd4Wx9H7Bkgd4ydsMC+Sh8wk05jOV3qS9j9cYTNkRKXOny9rK7lZJIQcH8fPYpIe48kAwZPmg==", + "dependencies": { + "Microsoft.VisualStudio.Shell.Framework": "17.0.31902.203" + } + }, + "Microsoft.VisualStudio.ProjectAggregator": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "C96eQGjS87DX1yjl/C57YFpWX2hD2Vi0BlLnz44nFhAxCvQ2Fy5FN/E63yNMIdX+iAZkCoG80c1D7C6fBOL0XA==" + }, + "Microsoft.VisualStudio.RemoteControl": { + "type": "Transitive", + "resolved": "16.3.41", + "contentHash": "Q9lz2anDPJxDLznQRaybv21aY3qgQJmGJiUonH8z2D0XAgKMlMelsu9bg9zhnKCxtA/jreRAM3Md2W6thiDOwQ==", + "dependencies": { + "Microsoft.VisualStudio.Utilities.Internal": "16.3.23" + } + }, + "Microsoft.VisualStudio.RpcContracts": { + "type": "Transitive", + "resolved": "17.0.51", + "contentHash": "4cMhOmJJl18BU+LTrcjNTLDqWBuCN5t87fB64n7UNyKLs03o4UVNKEjsUwlo6USbccGfoyba4mWPWIo7vL0qkA==", + "dependencies": { + "Microsoft.ServiceHub.Framework": "3.0.2061", + "StreamJsonRpc": "2.8.28" + } + }, + "Microsoft.VisualStudio.SDK": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "YSOMLjLm0k4Q5JeoPvyRrYaHdUzxkKtkt9Hn/0NjWDdQ7tLrhZjR3SOiaSMYOFYQCQ0G7v2UBIRHfbGAnt7SNg==", + "dependencies": { + "Microsoft.VisualStudio.CommandBars": "17.0.31902.203", + "Microsoft.VisualStudio.ComponentModelHost": "17.0.487", + "Microsoft.VisualStudio.CoreUtility": "17.0.487", + "Microsoft.VisualStudio.Debugger.Interop.12.0": "17.0.31902.203", + "Microsoft.VisualStudio.Debugger.Interop.14.0": "17.0.31902.203", + "Microsoft.VisualStudio.Debugger.Interop.15.0": "17.0.31902.203", + "Microsoft.VisualStudio.Debugger.Interop.16.0": "17.0.31902.203", + "Microsoft.VisualStudio.Designer.Interfaces": "17.0.31902.203", + "Microsoft.VisualStudio.Editor": "17.0.487", + "Microsoft.VisualStudio.ImageCatalog": "17.0.31902.203", + "Microsoft.VisualStudio.Imaging": "17.0.31902.203", + "Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime": "17.0.31902.203", + "Microsoft.VisualStudio.Interop": "17.0.31902.203", + "Microsoft.VisualStudio.Language": "17.0.487", + "Microsoft.VisualStudio.Language.Intellisense": "17.0.487", + "Microsoft.VisualStudio.Language.NavigateTo.Interfaces": "17.0.487", + "Microsoft.VisualStudio.Language.StandardClassification": "17.0.487", + "Microsoft.VisualStudio.LanguageServer.Client": "17.0.5158", + "Microsoft.VisualStudio.OLE.Interop": "17.0.31902.203", + "Microsoft.VisualStudio.Package.LanguageService.15.0": "17.0.31902.203", + "Microsoft.VisualStudio.ProjectAggregator": "17.0.31902.203", + "Microsoft.VisualStudio.Setup.Configuration.Interop": "3.0.4492", + "Microsoft.VisualStudio.Shell.Design": "17.0.31902.203", + "Microsoft.VisualStudio.Shell.Interop": "17.0.31902.203", + "Microsoft.VisualStudio.Shell.Interop.10.0": "17.0.31902.203", + "Microsoft.VisualStudio.Shell.Interop.11.0": "17.0.31902.203", + "Microsoft.VisualStudio.Shell.Interop.12.0": "17.0.31902.203", + "Microsoft.VisualStudio.Shell.Interop.8.0": "17.0.31902.203", + "Microsoft.VisualStudio.Shell.Interop.9.0": "17.0.31902.203", + "Microsoft.VisualStudio.TaskRunnerExplorer.14.0": "14.0.0", + "Microsoft.VisualStudio.Text.Logic": "17.0.487", + "Microsoft.VisualStudio.TextManager.Interop": "17.0.31902.203", + "Microsoft.VisualStudio.TextManager.Interop.10.0": "17.0.31902.203", + "Microsoft.VisualStudio.TextManager.Interop.11.0": "17.0.31902.203", + "Microsoft.VisualStudio.TextManager.Interop.12.0": "17.0.31902.203", + "Microsoft.VisualStudio.TextManager.Interop.8.0": "17.0.31902.203", + "Microsoft.VisualStudio.TextManager.Interop.9.0": "17.0.31902.203", + "Microsoft.VisualStudio.TextTemplating.VSHost": "17.0.31902.203", + "Microsoft.VisualStudio.VCProjectEngine": "17.0.31902.203", + "Microsoft.VisualStudio.VSHelp": "17.0.31902.203", + "Microsoft.VisualStudio.VSHelp80": "17.0.31902.203", + "Microsoft.VisualStudio.Validation": "17.0.28", + "Microsoft.VisualStudio.WCFReference.Interop": "17.0.31902.203", + "Microsoft.VisualStudio.Web.BrowserLink.12.0": "12.0.0", + "Microsoft.Win32.Primitives": "4.3.0", + "System.ComponentModel.Composition": "4.5.0", + "VSLangProj": "17.0.31902.203", + "VSLangProj100": "17.0.31902.203", + "VSLangProj110": "17.0.31902.203", + "VSLangProj140": "17.0.31902.203", + "VSLangProj150": "17.0.31902.203", + "VSLangProj157": "17.0.31902.203", + "VSLangProj158": "17.0.31902.203", + "VSLangProj165": "17.0.31902.203", + "VSLangProj2": "17.0.31902.203", + "VSLangProj80": "17.0.31902.203", + "VSLangProj90": "17.0.31902.203", + "envdte": "17.0.31902.203", + "envdte100": "17.0.31902.203", + "envdte80": "17.0.31902.203", + "envdte90": "17.0.31902.203", + "envdte90a": "17.0.31902.203", + "stdole": "17.0.31902.203" + } + }, + "Microsoft.VisualStudio.Setup.Configuration.Interop": { + "type": "Transitive", + "resolved": "3.0.4492", + "contentHash": "BfkqM96P8+N+cz4T+pxKrIKk2ZD1YMxCXH2ivtBDj5tx6Mc2YQLK1+3h+C6Qebper0RBipuHVn51lb9SZH6bKQ==" + }, + "Microsoft.VisualStudio.Shell.15.0": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "9bOKENGcO474GIgVoRzmkce6FdW/fiH/W2M0ULyRkFjNh9yEdJheYeyJFBr3Pb0EAwPwu2Q3gayQB7I9oTd/SA==", + "dependencies": { + "Microsoft.VisualStudio.ComponentModelHost": "17.0.487", + "Microsoft.VisualStudio.ImageCatalog": "17.0.31902.203", + "Microsoft.VisualStudio.Imaging": "17.0.31902.203", + "Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime": "17.0.31902.203", + "Microsoft.VisualStudio.Interop": "17.0.31902.203", + "Microsoft.VisualStudio.ProjectAggregator": "17.0.31902.203", + "Microsoft.VisualStudio.Shell.Framework": "17.0.31902.203", + "Microsoft.VisualStudio.Text.Data": "17.0.487", + "Microsoft.VisualStudio.Utilities": "17.0.31902.203" + } + }, + "Microsoft.VisualStudio.Shell.Design": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "gFVdvJ9HOOnq4ntBCcULraptK+V+Mu1GahUXmd0dhsomGzkzgqDqN7aYQ3ys+K45pJhNeB3MJ/NNQUI4woyiBQ==", + "dependencies": { + "Microsoft.VisualStudio.ImageCatalog": "17.0.31902.203", + "Microsoft.VisualStudio.Interop": "17.0.31902.203", + "Microsoft.VisualStudio.Shell.15.0": "17.0.31902.203", + "Microsoft.VisualStudio.Shell.Framework": "17.0.31902.203" + } + }, + "Microsoft.VisualStudio.Shell.Framework": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "L9s0/zSMeNlh1Pyzh9vX7T2+O3vg0JxTgXHyXGP/36DpJh4f+87D4rj+/nkESY4YXIUKPVl7XWXh3NLG0yjj1w==", + "dependencies": { + "Microsoft.Build.Framework": "16.5.0", + "Microsoft.VisualStudio.GraphModel": "17.0.31902.203", + "Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime": "17.0.31902.203", + "Microsoft.VisualStudio.Interop": "17.0.31902.203", + "Microsoft.VisualStudio.Telemetry": "16.3.250", + "Microsoft.VisualStudio.Text.Data": "17.0.487", + "Microsoft.VisualStudio.Threading": "17.0.64", + "Microsoft.VisualStudio.Utilities": "17.0.31902.203", + "Newtonsoft.Json": "13.0.1", + "System.ComponentModel.Composition": "4.5.0", + "System.Threading.Tasks.Dataflow": "5.0.0", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.VisualStudio.Shell.Interop": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "GaGSme4K2Wy0b65evinVk7JCl1+Dn7TCxBBwKuzATjWWiY0lvvqLElhXBq+qpC23YN0I5jTBTv0inhZ3G06uMg==", + "dependencies": { + "Microsoft.VisualStudio.Interop": "17.0.31902.203" + } + }, + "Microsoft.VisualStudio.Shell.Interop.10.0": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "CiHRisP0ts0lbyZ1SR0H7zfiVDTgkQTeORQTDY1xOTy7WL1hIzqmUz/M5UL7d6WcVI4hTAFosJ77NcqYrnTwGg==", + "dependencies": { + "Microsoft.VisualStudio.Interop": "17.0.31902.203" + } + }, + "Microsoft.VisualStudio.Shell.Interop.11.0": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "doH7HCWzS2bXohKR0Vl1DRjzqV0iw/poZteLS0i4nzPqFs8e2xg+U6a/ysUQ3QHgZ5y7iraefPPmp/VMJfbIeQ==", + "dependencies": { + "Microsoft.VisualStudio.Interop": "17.0.31902.203" + } + }, + "Microsoft.VisualStudio.Shell.Interop.12.0": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "SfZOz3igcZc7mKOBOr2MrPnliJL0HWF0s9UAL1B05Rc8cI/wBNIbyC1PE8Xt44P6c1vjJm6tWscAFSHZdKpGuA==", + "dependencies": { + "Microsoft.VisualStudio.Interop": "17.0.31902.203" + } + }, + "Microsoft.VisualStudio.Shell.Interop.8.0": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "rLDc0KVPc1HZEWd5OYyvKGJ2+0eay9gNeh2wMiEJC7UNLHo+JM0wxAYNehze50hWJKlFqefHKJ6Klmp1iX3kpQ==", + "dependencies": { + "Microsoft.VisualStudio.Interop": "17.0.31902.203" + } + }, + "Microsoft.VisualStudio.Shell.Interop.9.0": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "bz8tlDefGkBIGfF5KSScfs6f7P79IYzX3mqKAlXUshZjYQNd+8hLngls19cCP4oNlOre/DXAhESptNOgVihzzg==", + "dependencies": { + "Microsoft.VisualStudio.Interop": "17.0.31902.203" + } + }, + "Microsoft.VisualStudio.TaskRunnerExplorer.14.0": { + "type": "Transitive", + "resolved": "14.0.0", + "contentHash": "iZpAv8bEWjkyxFF1GIcSOfldqP/umopJKnJGKHa0vg8KR7ZY3u3dWtJmwO4w3abIx+176SIkQe78y5A+/Md7FA==" + }, + "Microsoft.VisualStudio.Telemetry": { + "type": "Transitive", + "resolved": "16.3.250", + "contentHash": "Ijo4HUinCwSdgegeXXzfEYmWuVLC1o9CI3FXW2x4CPKoYemlrN6xcGDUy4oKRxyOsFkjAaiRxR/X9TOgy2xVsQ==", + "dependencies": { + "Microsoft.CSharp": "4.7.0", + "Microsoft.VisualStudio.RemoteControl": "16.3.41", + "Microsoft.VisualStudio.Utilities.Internal": "16.3.23", + "Newtonsoft.Json": "9.0.1" + } + }, + "Microsoft.VisualStudio.Text.Data": { + "type": "Transitive", + "resolved": "17.0.487", + "contentHash": "lrqBhf4lI8Xzk0Z5hwcAtqnYTL1OK+iwu6tHJXDR8Vv7hFvG/YwetOQMJPz6UaLd93PEX+jPh0IfdY/CddoVDQ==", + "dependencies": { + "Microsoft.VisualStudio.CoreUtility": "17.0.487", + "Microsoft.VisualStudio.Threading": "17.0.63" + } + }, + "Microsoft.VisualStudio.Text.Logic": { + "type": "Transitive", + "resolved": "17.0.487", + "contentHash": "ZLMg3fDYijfur0B02/tkB1lmUWQtRfC9xkRr2XlQ/x7MOotTvDQO3HJp9hZyqn9LVBTWbjOnLVcoSmzthsGnnw==", + "dependencies": { + "Microsoft.VisualStudio.CoreUtility": "17.0.487", + "Microsoft.VisualStudio.Text.Data": "17.0.487", + "System.Collections.Immutable": "5.0.0", + "System.ComponentModel.Composition": "4.5.0" + } + }, + "Microsoft.VisualStudio.Text.UI": { + "type": "Transitive", + "resolved": "17.0.487", + "contentHash": "f+7L6k+eyJtQzrr//j2ZtYRDnGJeGSluT10do9/3ajgfT3WpRdlq5HEr60imnHbUU5eY2V/bjbKIu8q6Yg2VfA==", + "dependencies": { + "Microsoft.VisualStudio.CoreUtility": "17.0.487", + "Microsoft.VisualStudio.Text.Data": "17.0.487", + "Microsoft.VisualStudio.Text.Logic": "17.0.487", + "System.ComponentModel.Composition": "4.5.0" + } + }, + "Microsoft.VisualStudio.Text.UI.Wpf": { + "type": "Transitive", + "resolved": "17.0.487", + "contentHash": "Aq+eNxbpBlU8RYsSwh2JM1ZsttVvU7BKgFG6I/356EaY1ZJVz2LhAfjAl6C/A3pmizxgcczQZOIIJsrhcP/OIQ==", + "dependencies": { + "Microsoft.VisualStudio.CoreUtility": "17.0.487", + "Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime": "17.0.31723.112", + "Microsoft.VisualStudio.Text.Data": "17.0.487", + "Microsoft.VisualStudio.Text.Logic": "17.0.487", + "Microsoft.VisualStudio.Text.UI": "17.0.487" + } + }, + "Microsoft.VisualStudio.TextManager.Interop": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "w4+TDaisrknpeXkeebsTQsgl+6gbJivFyZp18/7Yqpo/lyG+IttvE9ZnJ5fFofTyrmgSGLi9BLEifT0mtsEaXA==", + "dependencies": { + "Microsoft.VisualStudio.Interop": "17.0.31902.203" + } + }, + "Microsoft.VisualStudio.TextManager.Interop.10.0": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "ODXNlirs3ttpe1ooEDsKKw6XbQ06h3kvJua++J0gPqM6vMWS5YfJVH0UxHQ+BDAFZQnUJcbH9b0sVdC9JUKQ6g==", + "dependencies": { + "Microsoft.VisualStudio.Interop": "17.0.31902.203" + } + }, + "Microsoft.VisualStudio.TextManager.Interop.11.0": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "7NvcHy27r3fmlgdxPrZy4LWAm3yFqNJB3vMTMJv0K/olUzIpNRXYJMeQNltb8AJuOrbpb6HV6L2MIyJDehEADg==", + "dependencies": { + "Microsoft.VisualStudio.Interop": "17.0.31902.203" + } + }, + "Microsoft.VisualStudio.TextManager.Interop.12.0": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "2ptvBpyBVEtxwUS+FfFKPjbj+l6u/3IyznQegMmEIoQda6oeg07TFL0VAVmsqg48s4FFtpqRklx8kWhMNZqSXQ==", + "dependencies": { + "Microsoft.VisualStudio.Interop": "17.0.31902.203" + } + }, + "Microsoft.VisualStudio.TextManager.Interop.8.0": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "70mNZyDPIsC+rNov2wHYb6H+V8Woc/FJktj2JsA194eMxk+DPpF0Jx3/sMgkDycV+CjIJog6TguywF4q4milIA==", + "dependencies": { + "Microsoft.VisualStudio.Interop": "17.0.31902.203" + } + }, + "Microsoft.VisualStudio.TextManager.Interop.9.0": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "s59cD2jJpwEGs6HE0fbp0sE+ZsR4pp8bK91WCelRUVCiMNMtgzb3PVBCjvl082f9UxIsClOBBYDTUjrt/CFqVQ==", + "dependencies": { + "Microsoft.VisualStudio.Interop": "17.0.31902.203" + } + }, + "Microsoft.VisualStudio.TextTemplating": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "IYWBsY2RWM8p2blyT0ja94+YRSc0wmL5er4/DfQShc3JkrQpVmmnWwSjRWC5IEp/dSukMW1LEfStqi/Kc6ZTtQ==", + "dependencies": { + "Microsoft.VisualStudio.TextTemplating.Interfaces": "17.0.31902.203" + } + }, + "Microsoft.VisualStudio.TextTemplating.Interfaces": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "rCQLkYDAKJ4BGot0CL5dkYhwArR3x2COIiTF5z3cMONVVfQqoXJKXaXve/O22DaTE/28Zt5EnaDNzZHIwROpeg==", + "dependencies": { + "Microsoft.VisualStudio.TextTemplating.Interfaces.11.0": "17.0.31902.203" + } + }, + "Microsoft.VisualStudio.TextTemplating.Interfaces.10.0": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "KsY6a4Nk+wOj/0aGAE21JSglQt6+tlsAyPnVyiqqHn+ir1Wx9o0rFtekc6EfSle8nn5RM7QqLGSUR/Bxy6rQbA==" + }, + "Microsoft.VisualStudio.TextTemplating.Interfaces.11.0": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "pDYRlZTH42mS+r+dIjce6KOt/3HSxX1M1tPldT8VySnQ2PsT/6kRK2UVMOPla9hfJ1dWj2wMpYF8UNN2nuzdjA==", + "dependencies": { + "Microsoft.VisualStudio.TextTemplating.Interfaces.10.0": "17.0.31902.203" + } + }, + "Microsoft.VisualStudio.TextTemplating.VSHost": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "srtrkd9VCmGFRvMNV6STSe0XqM/DSl7xfY+EUIWhPVO8k+dl+DBNPIZzCK43Enfb+i3OrYFY2yqB6baNaelnaA==", + "dependencies": { + "Microsoft.VisualStudio.Shell.Framework": "17.0.31902.203", + "Microsoft.VisualStudio.TextTemplating": "17.0.31902.203", + "Microsoft.VisualStudio.Validation": "17.0.28", + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, + "Microsoft.VisualStudio.Threading": { + "type": "Transitive", + "resolved": "17.0.64", + "contentHash": "HD/yoC7u1Ignj/EsCST4iFXl8zaE+8r2A+4CUkl6GLTJjdNjfl8iNvhqpyK8+DjCMwhyNRRH0I6S6FA37fz95Q==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "Microsoft.VisualStudio.Threading.Analyzers": "17.0.64", + "Microsoft.VisualStudio.Validation": "16.10.35", + "Microsoft.Win32.Registry": "5.0.0", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.VisualStudio.Threading.Analyzers": { + "type": "Transitive", + "resolved": "17.0.64", + "contentHash": "+xz3lAqA3h2/5q6H7Udmz9TsxDQ99O+PjoQ4k4BTO3SfAfyJX7ejh7I1D1N/M/GzGUci9YOUpr6KBO4vXLg+zQ==" + }, + "Microsoft.VisualStudio.Utilities": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "j0c5dq1ZmFFAi4kB4wJ1BOUrb1kJyhFvaqDKymbdJNaib8DdG8PNAdLrW1l0RewsEMDy5Rh2cO7mzrG/FlyE5Q==", + "dependencies": { + "Microsoft.ServiceHub.Client": "3.0.3078", + "Microsoft.VisualStudio.RpcContracts": "17.0.51", + "Microsoft.VisualStudio.Telemetry": "16.3.250", + "System.ComponentModel.Composition": "4.5.0", + "System.Threading.AccessControl": "5.0.0", + "System.Threading.Tasks.Dataflow": "5.0.0" + } + }, + "Microsoft.VisualStudio.Utilities.Internal": { + "type": "Transitive", + "resolved": "16.3.23", + "contentHash": "AxbS8vXJj0IjTv67JbmOqwJERYUDE7BHbXYkXGiyqYblizMjhVdohNIethnJX9lVN2RmotN5GQbwLWDoMKatvw==" + }, + "Microsoft.VisualStudio.Validation": { + "type": "Transitive", + "resolved": "17.0.28", + "contentHash": "qT+0Qv7lxLt7NKQjkroi34s8cDXVPWA3vDkvoFZwM9PRmZ28aKrMLaQRnkT7rgBYLf+mNtr2najktKUzkAtP6Q==" + }, + "Microsoft.VisualStudio.VCProjectEngine": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "QjjZguY7FypxVasdPihUbVSP5PjwBPiitCgnC/ZPePFO5KYzJ99VxdlASDDxNGVq5+q/+3YlBD94PXComDRu2w==" + }, + "Microsoft.VisualStudio.VSHelp": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "PT4aoOrZfPHIpTq3lwamypIrx0hR7d5iLlHewwhjUNezQ8ElN/jbWjvx74r/uf2g6dVPiQcS4HI0RGgAb4MQ2A==", + "dependencies": { + "Microsoft.VisualStudio.Interop": "17.0.31902.203" + } + }, + "Microsoft.VisualStudio.VSHelp80": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "B/G69KkAcMIwXgEvGAMpGSq1PvMx9JKAVzQdKiX4gm2DMFEsT2Id89TxS0oPg2X7QC3JBCk+/+BDUjtWPkjqMA==", + "dependencies": { + "Microsoft.VisualStudio.Interop": "17.0.31902.203" + } + }, + "Microsoft.VisualStudio.WCFReference.Interop": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "45HixSYn5IBSgc6MmVHhivWCiRgdZ8BruXOC9fUrQIRrYHu8DR9aw8imnFImmJY3Mii92VHV4NKPYOgUkXNd/Q==", + "dependencies": { + "Microsoft.VisualStudio.Interop": "17.0.31902.203" + } + }, + "Microsoft.VisualStudio.Web.BrowserLink.12.0": { + "type": "Transitive", + "resolved": "12.0.0", + "contentHash": "HeuaZh8+wNVdwx7VF8guFGH2Z2zH+FYxWBsRNp+FjjlmrhCfM7GUQV5azaTv/bN5TPaK8ALJoP9UX5o1FB5k1A==" + }, + "Microsoft.Web.Xdt": { + "type": "Transitive", + "resolved": "2.1.0", + "contentHash": "/ieJ02r4MEJM21Eyl+c5kwoJWPhy+qEEcN68JaqDoamabgxJI1jGi/kNLuKvHUCl6tU7E0rMvaR6FmEnDWtS4A==" + }, + "Microsoft.Win32.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "9ZQKCWxH7Ijp9BfahvL2Zyf1cJIk8XYLF6Yjzr2yi0b2cOut/HQ31qf1ThHAgCc3WiZMdnWcfJCgN82/0UunxA==" + }, + "Microsoft.Win32.Registry": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "Nerdbank.Streams": { + "type": "Transitive", + "resolved": "2.6.81", + "contentHash": "htBHFE359qyyFwrvAGvFxrbBAoldZdl0XjtQdDWTJ8t5sWWs7QVXID5y1ZGJE61UgpV5CqWsj/NT0LOAn5GdZw==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "1.1.1", + "Microsoft.VisualStudio.Threading": "16.7.56", + "Microsoft.VisualStudio.Validation": "15.5.31", + "System.IO.Pipelines": "4.7.2", + "System.Net.WebSockets": "4.3.0", + "System.Runtime.CompilerServices.Unsafe": "4.7.1" + } + }, "Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.3", "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" }, + "NuGet.Core": { + "type": "Transitive", + "resolved": "2.12.0", + "contentHash": "kSDD1VFIq7BBrimjMRk9HeeQlZteyknbHgz3AT4AUVUCZ8BZaCSzO1A5UTVPLLbSHryjozPcEmNYSds/IDAIjw==", + "dependencies": { + "Microsoft.Web.Xdt": "2.1.0" + } + }, + "NuGet.VisualStudio": { + "type": "Transitive", + "resolved": "3.3.0", + "contentHash": "ZVBJ43/IxaAl5iX+OuuDLQ9tSNJaRlP85SUMAdiGoqEbyK/ZPfQmPfqSMZMFpucRlf8HgsEK/hR73DwpCmqcLA==" + }, + "stdole": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "c1WK5n8poXt1fafyuS+ntL5w0tc3P/r4F7+aeUaLYTp2/YoQB8XUpQFWqytZdtu7EAuMXoZ14T7jh7Vl+M4ZfQ==", + "dependencies": { + "Microsoft.VisualStudio.Interop": "17.0.31902.203" + } + }, + "StreamJsonRpc": { + "type": "Transitive", + "resolved": "2.8.28", + "contentHash": "i2hKUXJSLEoWpPqQNyISqLDqmFHMiyasjTC/PrrHNWhQyauFeVoebSct3E4OTUzRC1DYjVJ9AMiVbp/uVYLnjQ==", + "dependencies": { + "MessagePack": "2.2.85", + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "Microsoft.VisualStudio.Threading": "16.9.60", + "Nerdbank.Streams": "2.6.81", + "Newtonsoft.Json": "12.0.2", + "System.Collections.Immutable": "5.0.0", + "System.Diagnostics.DiagnosticSource": "5.0.1", + "System.IO.Pipelines": "5.0.1", + "System.Memory": "4.5.4", + "System.Net.Http": "4.3.4", + "System.Net.WebSockets": "4.3.0", + "System.Reflection.Emit": "4.7.0", + "System.Threading.Tasks.Dataflow": "5.0.0", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "StrongNamer": { + "type": "Transitive", + "resolved": "0.0.8", + "contentHash": "7GAMIhQSKabPrXVtEpDQoLRWVZUn1IGzeHvfDSvs5lPhU4KPFMQVpI/lDFikhMXr8FmdFBOBGaHEhQ9azMK3TA==" + }, "System.Buffers": { "type": "Transitive", "resolved": "4.5.1", @@ -94,11 +989,48 @@ "System.Memory": "4.5.4" } }, + "System.ComponentModel.Composition": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "+iB9FoZnfdqMEGq6np28X6YNSUrse16CakmIhV3h6PxEWt7jYxUN3Txs1D8MZhhf4QmyvK0F/EcIN0f4gGN0dA==" + }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "5.0.1", + "contentHash": "uXQEYqav2V3zP6OwkOKtLv+qIi6z3m1hsGyKwXX7ZA7htT4shoVccGxnJ9kVRFPNAsi1ArZTq2oh7WOto6GbkQ==", + "dependencies": { + "System.Memory": "4.5.4", + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, + "System.IO": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==" + }, "System.IO.Abstractions": { "type": "Transitive", "resolved": "9.0.4", "contentHash": "1h4krG51ZiW/CGzM8gtqrRW2oeG6WZDfPaj27suexL8PxBVahsUlUKMJrqI4kkh6ggHLSDd7MFeU8orpk6COZg==" }, + "System.IO.Abstractions.TestingHelpers": { + "type": "Transitive", + "resolved": "9.0.4", + "contentHash": "jygAL7t8okBFhnxzAP7gxDHKCrT2UZWcJCvXfFSf2M9QfOoryfvMyvRYIdu7B/+wYPrCbhSOJDQy5pwdy0bhQg==", + "dependencies": { + "System.IO.Abstractions": "9.0.4" + } + }, + "System.IO.Pipelines": { + "type": "Transitive", + "resolved": "5.0.1", + "contentHash": "qEePWsaq9LoEEIqhbGe6D5J8c9IqQOUuTzzV6wn1POlfdLkJliZY3OlB0j0f17uMWlqZYjH7txj+2YbyrIA8Yg==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, "System.Memory": { "type": "Transitive", "resolved": "4.5.4", @@ -109,15 +1041,120 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.3" } }, + "System.Net.Http": { + "type": "Transitive", + "resolved": "4.3.4", + "contentHash": "aOa2d51SEbmM+H+Csw7yJOuNZoHkrP2XnAurye5HWYgGVVU54YZDvsLUYRv6h18X3sPnjNCANmN7ZhIPiqMcjA==", + "dependencies": { + "System.Security.Cryptography.X509Certificates": "4.3.0" + } + }, + "System.Net.WebSockets": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "u6fFNY5q4T8KerUAVbya7bR6b7muBuSTAersyrihkcmE5QhEOiH3t5rh4il15SexbVlpXFHGuMwr/m8fDrnkQg==" + }, "System.Numerics.Vectors": { "type": "Transitive", "resolved": "4.5.0", "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" }, + "System.Private.Uri": { + "type": "Transitive", + "resolved": "4.3.2", + "contentHash": "o1+7RJnu3Ik3PazR7Z7tJhjPdE000Eq2KGLLWhqJJKXj04wrS8lwb1OFtDF9jzXXADhUuZNJZlPc98uwwqmpFA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.1", + "Microsoft.NETCore.Targets": "1.1.3" + } + }, + "System.Reflection.Emit": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "VR4kk8XLKebQ4MZuKuIni/7oh+QGFmZW3qORd1GvBq/8026OpW501SzT/oypwiQl4TvT8ErnReh/NzY9u+C6wQ==" + }, + "System.Reflection.Emit.Lightweight": { + "type": "Transitive", + "resolved": "4.6.0", + "contentHash": "j/V5HVvxvBQ7uubYD0PptQW2KGsi1Pc2kZ9yfwLixv3ADdjL/4M78KyC5e+ymW612DY8ZE4PFoZmWpoNmN2mqg==" + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "5NecZgXktdGg34rh1OenY1rFNDCI8xSjFr+Z4OU4cU06AQHUdRnIIEeWENu3Wl4YowbzkymAIMvi3WyK9U53pQ==", + "dependencies": { + "System.Collections.Immutable": "5.0.0" + } + }, + "System.Runtime": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==" + }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "4.5.3", - "contentHash": "3TIsJhD1EiiT0w2CcDMN/iSSwnNnsrnbzeVHSKkaEgV85txMprmuO+Yq2AdSbeVGcg28pdNDTPK87tJhX7VFHw==" + "resolved": "5.0.0", + "contentHash": "ZD9TMpsmYJLrxbbmdvhwt9YEgG5WntEnZ/d1eH8JBX9LBp+Ju8BSBhUGbZMNVHHomWo2KVImJhTDl2hIgw/6MA==" + }, + "System.Security.AccessControl": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "dependencies": { + "System.Security.Principal.Windows": "5.0.0" + } + }, + "System.Security.Cryptography.Algorithms": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==", + "dependencies": { + "System.IO": "4.3.0", + "System.Runtime": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0" + } + }, + "System.Security.Cryptography.Encoding": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==" + }, + "System.Security.Cryptography.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==" + }, + "System.Security.Cryptography.X509Certificates": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==", + "dependencies": { + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0" + } + }, + "System.Security.Principal.Windows": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" + }, + "System.Text.Encoding.CodePages": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "4J2JQXbftjPMppIHJ7IC+VXQ9XfEagN92vZZNoG12i+zReYlim5dMoXFC1Zzg7tsnKDM7JPo5bYfFK4Jheq44w==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "4.5.2" + } + }, + "System.Threading.AccessControl": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "WJ9w9m4iHJVq0VoH7hZvYAccbRq95itYRhAAXd6M4kVCzLmT6NqTwmSXKwp3oQilWHhYTXgqaIXxBfg8YaqtmA==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } }, "System.Threading.Channels": { "type": "Transitive", @@ -127,6 +1164,11 @@ "System.Threading.Tasks.Extensions": "4.5.4" } }, + "System.Threading.Tasks.Dataflow": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "NBp0zSAMZp4muDje6XmbDfmkqw9+qsDCHp+YMEtnVgHEjQZ3Q7MzFTTp3eHqpExn4BwMrS7JkUVOTcVchig4Sw==" + }, "System.Threading.Tasks.Extensions": { "type": "Transitive", "resolved": "4.5.4", @@ -135,6 +1177,106 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.3" } }, + "VSLangProj": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "XjyhxIyXjPitKrxeWkF1yaiqT02Klct5yZHz93x9O4nLTEnbmAHb+HRNiIIgNMXvG2sboNIvTt1MpuR1AeJktw==", + "dependencies": { + "Microsoft.VisualStudio.Interop": "17.0.31902.203" + } + }, + "VSLangProj100": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "8pwHJP2XefsWxhL3dQ11MBD9WwxP3SjAO41t6ZcFSfTHtSQFsp7erdtBrKzuFXpnIp3WoVc26f7jWrt4rr66+g==", + "dependencies": { + "Microsoft.VisualStudio.Interop": "17.0.31902.203" + } + }, + "VSLangProj110": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "UToccqkFdocw9LZx7Rn62/puqxPjF7MQWIk2yMGqIdHLcaeRmR6gI1/3Ea+57ZcVehhuEvtxH8jXV2t4HwwU8g==", + "dependencies": { + "Microsoft.VisualStudio.Interop": "17.0.31902.203" + } + }, + "VSLangProj140": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "ssRCHLYW5nSJJBy3rad5JId8SjcXHOsOzVW4ohZgtnHBtf73ehIJLtr+oRc7JK3NasmWBuB9DJqwwJBLj4wn3Q==", + "dependencies": { + "Microsoft.VisualStudio.Interop": "17.0.31902.203" + } + }, + "VSLangProj150": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "sxFOk5x5bDqyxUgfi5zF58E8BnfRQ17Ft0IbxJDS0Bvf+6CLYjWQzKXqxCczYHs2aD7upUpN1hrf93bHlqahAQ==", + "dependencies": { + "Microsoft.VisualStudio.Interop": "17.0.31902.203" + } + }, + "VSLangProj157": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "jYp9X00aaJKttYL1IuME8mpvd349H2zDbaFIBRf6LnDUPHd7i7MsnaREpID1Dr+H37P16pzbv48taLKUnAoBsw==", + "dependencies": { + "Microsoft.VisualStudio.Interop": "17.0.31902.203" + } + }, + "VSLangProj158": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "bwHHmgclOjBNI/CpUqgPH274D7UKDrgzFAEtFULixlOhA6E4NfYOgdUdemiW5bg58i4ao60T9tz3ac6PQfHeAw==", + "dependencies": { + "Microsoft.VisualStudio.Interop": "17.0.31902.203" + } + }, + "VSLangProj165": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "Brh37fZOyPL0zC9XVtOzWRl22GZZnN6/TVEvg6PDhWhjgnkdHv8hz0GZCIzilbyl9ilOJnQCx8oVQ+KMfcvXFQ==", + "dependencies": { + "Microsoft.VisualStudio.Interop": "17.0.31902.203" + } + }, + "VSLangProj2": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "hqP1IODtJneTrUwwXeqD0YHmqyde3/KJaJRJehsn07TQxYxzyigrpNsh8GEoExTSis0SoFt97tXTztbXiBvSvg==", + "dependencies": { + "Microsoft.VisualStudio.Interop": "17.0.31902.203" + } + }, + "VSLangProj80": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "qm0T7dPige52L6mnsk+3Q8QV8R/Rgdkdpb3FaUMolP4JWU4s2OfrUzFajFBf8auf/NBQgUcBvE1Kr5yRpYmL2Q==", + "dependencies": { + "Microsoft.VisualStudio.Interop": "17.0.31902.203" + } + }, + "VSLangProj90": { + "type": "Transitive", + "resolved": "17.0.31902.203", + "contentHash": "KoyI+jvJ9fHwR2uGGlUxOqAmTgDO1naxwXbt504y/Jx1h/BXnhCWipGdyM/lS83GUGsmx8RhZbiiMlZw7LmYhw==", + "dependencies": { + "Microsoft.VisualStudio.Interop": "17.0.31902.203" + } + }, + "SonarLint.VisualStudio.ConnectedMode": { + "type": "Project", + "dependencies": { + "Microsoft.Alm.Authentication": "[4.0.0.1, )", + "Microsoft.VisualStudio.Sdk": "[17.0.31902.203, )", + "Newtonsoft.Json": "[13.0.3, )", + "SonarLint.VisualStudio.Core": "[1.0.0, )", + "SonarLint.VisualStudio.IssueVisualization": "[1.0.0, )", + "SonarLint.VisualStudio.SLCore": "[1.0.0, )", + "StrongNamer": "[0.0.8, )" + } + }, "SonarLint.VisualStudio.Core": { "type": "Project", "dependencies": { @@ -145,11 +1287,84 @@ "System.Threading.Channels": "[7.0.0, )" } }, + "SonarLint.VisualStudio.Infrastructure.VS": { + "type": "Project", + "dependencies": { + "Microsoft.VisualStudio.Sdk": "[17.0.31902.203, )", + "SonarLint.VisualStudio.Core": "[1.0.0, )" + } + }, + "SonarLint.VisualStudio.Integration": { + "type": "Project", + "dependencies": { + "Microsoft.Alm.Authentication": "[4.0.0.1, )", + "Microsoft.VisualStudio.Sdk": "[17.0.31902.203, )", + "Newtonsoft.Json": "[13.0.3, )", + "NuGet.Core": "[2.12.0, )", + "NuGet.VisualStudio": "[3.3.0, )", + "SonarLint.VisualStudio.ConnectedMode": "[1.0.0, )", + "SonarLint.VisualStudio.Core": "[1.0.0, )", + "SonarLint.VisualStudio.Infrastructure.VS": "[1.0.0, )", + "SonarQube.Client": "[1.0.0, )", + "StrongNamer": "[0.0.8, )", + "System.IO.Abstractions": "[9.0.4, )" + } + }, + "SonarLint.VisualStudio.Integration.TestInfrastructure": { + "type": "Project", + "dependencies": { + "FluentAssertions": "[5.9.0, )", + "FluentAssertions.Analyzers": "[0.11.4, )", + "MSTest.TestAdapter": "[1.4.0, )", + "MSTest.TestFramework": "[1.4.0, )", + "Microsoft.Alm.Authentication": "[4.0.0.1, )", + "Microsoft.NET.Test.Sdk": "[16.6.1, )", + "Microsoft.VisualStudio.Sdk": "[17.0.31902.203, )", + "Moq": "[4.18.2, )", + "NSubstitute": "[5.1.0, )", + "NSubstitute.Analyzers.CSharp": "[1.0.17, )", + "NuGet.Core": "[2.12.0, )", + "NuGet.VisualStudio": "[3.3.0, )", + "SonarLint.VisualStudio.Core": "[1.0.0, )", + "SonarLint.VisualStudio.Infrastructure.VS": "[1.0.0, )", + "SonarLint.VisualStudio.Integration": "[1.0.0, )", + "SonarQube.Client": "[1.0.0, )", + "StrongNamer": "[0.0.8, )", + "System.IO.Abstractions.TestingHelpers": "[9.0.4, )" + } + }, + "SonarLint.VisualStudio.IssueVisualization": { + "type": "Project", + "dependencies": { + "Microsoft.VisualStudio.Sdk": "[17.0.31902.203, )", + "SonarLint.VisualStudio.Core": "[1.0.0, )", + "SonarLint.VisualStudio.Infrastructure.VS": "[1.0.0, )", + "SonarLint.VisualStudio.SLCore": "[1.0.0, )", + "SonarQube.Client": "[1.0.0, )" + } + }, "SonarLint.VisualStudio.RoslynAnalyzerServer": { "type": "Project", "dependencies": { "SonarLint.VisualStudio.Core": "[1.0.0, )" } + }, + "SonarLint.VisualStudio.SLCore": { + "type": "Project", + "dependencies": { + "SonarLint.VisualStudio.Core": "[1.0.0, )", + "StreamJsonRpc": "[2.6.121, )" + } + }, + "sonarqube.client": { + "type": "Project", + "dependencies": { + "Google.Protobuf": "[3.6.1, )", + "Grpc.Tools": "[1.4.1, )", + "Newtonsoft.Json": "[13.0.3, )", + "SonarLint.VisualStudio.Core": "[1.0.0, )", + "System.Net.Http": "[4.0.0, )" + } } } } diff --git a/src/RoslynAnalyzerServer/Analysis/DiagnosticDuplicatesComparer.cs b/src/RoslynAnalyzerServer/Analysis/DiagnosticDuplicatesComparer.cs new file mode 100644 index 000000000..74e6b8707 --- /dev/null +++ b/src/RoslynAnalyzerServer/Analysis/DiagnosticDuplicatesComparer.cs @@ -0,0 +1,69 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2025 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +namespace SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis; + +public class DiagnosticDuplicatesComparer : IEqualityComparer +{ + public static DiagnosticDuplicatesComparer Instance { get; } = new(); + + private DiagnosticDuplicatesComparer() + { + } + + public bool Equals(SonarDiagnostic? x, SonarDiagnostic? y) + { + if (ReferenceEquals(x, y)) + { + return true; + } + if (x is null) + { + return false; + } + if (y is null) + { + return false; + } + + return x.RuleKey == y.RuleKey && LocationEquals(x.PrimaryLocation, y.PrimaryLocation); + } + + public int GetHashCode(SonarDiagnostic obj) + { + unchecked + { + var hc = obj.RuleKey.GetHashCode(); + hc = (hc * 397) ^ obj.PrimaryLocation.FilePath.GetHashCode(); + hc = (hc * 397) ^ obj.PrimaryLocation.TextRange.StartLine; + hc = (hc * 397) ^ obj.PrimaryLocation.TextRange.StartLineOffset; + hc = (hc * 397) ^ obj.PrimaryLocation.TextRange.EndLine; + hc = (hc * 397) ^ obj.PrimaryLocation.TextRange.EndLineOffset; + return hc; + } + } + + private static bool LocationEquals(SonarDiagnosticLocation xPrimaryLocation, SonarDiagnosticLocation yPrimaryLocation) => + xPrimaryLocation.FilePath == yPrimaryLocation.FilePath && + xPrimaryLocation.TextRange.StartLine == yPrimaryLocation.TextRange.StartLine && + xPrimaryLocation.TextRange.EndLine == yPrimaryLocation.TextRange.EndLine && + xPrimaryLocation.TextRange.StartLineOffset == yPrimaryLocation.TextRange.StartLineOffset && + xPrimaryLocation.TextRange.EndLineOffset == yPrimaryLocation.TextRange.EndLineOffset; +} diff --git a/src/RoslynAnalyzerServer/Analysis/ISonarRoslynAnalysisCommand.cs b/src/RoslynAnalyzerServer/Analysis/ISonarRoslynAnalysisCommand.cs new file mode 100644 index 000000000..b7fafcebe --- /dev/null +++ b/src/RoslynAnalyzerServer/Analysis/ISonarRoslynAnalysisCommand.cs @@ -0,0 +1,30 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2025 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis.Wrappers; + +namespace SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis; + +internal interface ISonarRoslynAnalysisCommand +{ + Task> ExecuteAsync(ISonarRoslynCompilationWithAnalyzersWrapper compilation, CancellationToken token); +} diff --git a/src/RoslynAnalyzerServer/Analysis/ISonarRoslynAnalysisEngine.cs b/src/RoslynAnalyzerServer/Analysis/ISonarRoslynAnalysisEngine.cs new file mode 100644 index 000000000..e97243b74 --- /dev/null +++ b/src/RoslynAnalyzerServer/Analysis/ISonarRoslynAnalysisEngine.cs @@ -0,0 +1,32 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2025 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using System.Collections.Immutable; +using SonarLint.VisualStudio.Core; + +namespace SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis; + +internal interface ISonarRoslynAnalysisEngine +{ + Task> AnalyzeAsync( + List projectsAnalysis, + ImmutableDictionary sonarRoslynAnalysisConfigurations, + CancellationToken token); +} diff --git a/src/RoslynAnalyzerServer/Analysis/ISonarRoslynProjectCompilationProvider.cs b/src/RoslynAnalyzerServer/Analysis/ISonarRoslynProjectCompilationProvider.cs new file mode 100644 index 000000000..c53e6205d --- /dev/null +++ b/src/RoslynAnalyzerServer/Analysis/ISonarRoslynProjectCompilationProvider.cs @@ -0,0 +1,33 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2025 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using System.Collections.Immutable; +using SonarLint.VisualStudio.Core; +using SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis.Wrappers; + +namespace SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis; + +internal interface ISonarRoslynProjectCompilationProvider +{ + Task GetProjectCompilationAsync( + ISonarRoslynProjectWrapper project, + ImmutableDictionary sonarRoslynAnalysisConfigurations, + CancellationToken token); +} diff --git a/src/RoslynAnalyzerServer/Analysis/ISonarRoslynSolutionAnalysisCommandProvider.cs b/src/RoslynAnalyzerServer/Analysis/ISonarRoslynSolutionAnalysisCommandProvider.cs new file mode 100644 index 000000000..86415858b --- /dev/null +++ b/src/RoslynAnalyzerServer/Analysis/ISonarRoslynSolutionAnalysisCommandProvider.cs @@ -0,0 +1,26 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2025 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +namespace SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis; + +internal interface ISonarRoslynSolutionAnalysisCommandProvider +{ + List GetAnalysisCommandsForCurrentSolution(string[] filePaths); +} diff --git a/src/RoslynAnalyzerServer/Analysis/SequentialSonarRoslynAnalysisEngine.cs b/src/RoslynAnalyzerServer/Analysis/SequentialSonarRoslynAnalysisEngine.cs new file mode 100644 index 000000000..7adb15305 --- /dev/null +++ b/src/RoslynAnalyzerServer/Analysis/SequentialSonarRoslynAnalysisEngine.cs @@ -0,0 +1,66 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2025 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using System.Collections.Immutable; +using System.ComponentModel.Composition; +using System.IO; +using SonarLint.VisualStudio.Core; + +namespace SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis; + +[Export(typeof(ISonarRoslynAnalysisEngine))] +[PartCreationPolicy(CreationPolicy.Shared)] +[method: ImportingConstructor] +internal class SequentialSonarRoslynAnalysisEngine( + IRoslynDiagnosticsConverter diagnosticsConverter, + ISonarRoslynProjectCompilationProvider projectCompilationProvider, + ILogger logger) : ISonarRoslynAnalysisEngine +{ + private readonly ILogger logger = logger.ForContext("Roslyn Analysis", "Engine"); + + public async Task> AnalyzeAsync( + List projectsAnalysis, + ImmutableDictionary sonarRoslynAnalysisConfigurations, + CancellationToken token) + { + var uniqueDiagnostics = new HashSet(DiagnosticDuplicatesComparer.Instance); + foreach (var projectAnalysisCommands in projectsAnalysis) + { + var compilationWithAnalyzers = await projectCompilationProvider.GetProjectCompilationAsync(projectAnalysisCommands.Project, sonarRoslynAnalysisConfigurations, token); + + // todo SLVS-2467 issue streaming + foreach (var analysisCommand in projectAnalysisCommands.AnalysisCommands) + { + var diagnostics = await analysisCommand.ExecuteAsync(compilationWithAnalyzers, token); + + foreach (var diagnostic in diagnostics.Select(d => diagnosticsConverter.ConvertToSonarDiagnostic(d, compilationWithAnalyzers.Language))) + { + // todo SLVS-2468 improve issue merging + if (!uniqueDiagnostics.Add(diagnostic)) + { + logger.LogVerbose("Duplicate diagnostic discarded ID: {0}, File: {1}, Line: {2}", diagnostic.RuleKey, Path.GetFileName(diagnostic.PrimaryLocation.FilePath), diagnostic.PrimaryLocation.TextRange.StartLine); + } + } + } + } + + return uniqueDiagnostics; + } +} diff --git a/src/RoslynAnalyzerServer/Analysis/SonarDiagnostic.cs b/src/RoslynAnalyzerServer/Analysis/SonarDiagnostic.cs new file mode 100644 index 000000000..0828044a2 --- /dev/null +++ b/src/RoslynAnalyzerServer/Analysis/SonarDiagnostic.cs @@ -0,0 +1,62 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2025 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using SonarLint.VisualStudio.Core.Analysis; + +namespace SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis; + +public class SonarDiagnostic( + string ruleKey, + SonarDiagnosticLocation primaryLocation, + IReadOnlyList? flows = null, + IReadOnlyList? fixes = null) +{ + private static readonly IReadOnlyList EmptyFlows = []; + private static readonly IReadOnlyList EmptyFixes = []; + + public string RuleKey { get; } = ruleKey; + public SonarDiagnosticLocation PrimaryLocation { get; } = primaryLocation ?? throw new ArgumentNullException(nameof(primaryLocation)); + public IReadOnlyList Flows { get; } = flows ?? EmptyFlows; + public IReadOnlyList Fixes { get; } = fixes ?? EmptyFixes; +} + +public class SonarDiagnosticFlow(IReadOnlyList locations) +{ + public IReadOnlyList Locations { get; } = locations ?? throw new ArgumentNullException(nameof(locations)); +} + +public class SonarDiagnosticLocation(string message, string filePath, SonarTextRange textRange) +{ + public string FilePath { get; } = filePath; + public string Message { get; } = message; + public SonarTextRange TextRange { get; } = textRange; +} + +public class SonarTextRange( + int startLine, + int endLine, + int startLineOffset, + int endLineOffset) +{ + public int StartLine { get; } = startLine; + public int EndLine { get; } = endLine; + public int StartLineOffset { get; } = startLineOffset; + public int EndLineOffset { get; } = endLineOffset; +} diff --git a/src/RoslynAnalyzerServer/Analysis/SonarRoslynAnalysisConfiguration.cs b/src/RoslynAnalyzerServer/Analysis/SonarRoslynAnalysisConfiguration.cs new file mode 100644 index 000000000..065168881 --- /dev/null +++ b/src/RoslynAnalyzerServer/Analysis/SonarRoslynAnalysisConfiguration.cs @@ -0,0 +1,30 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2025 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis; + +public record SonarRoslynAnalysisConfiguration( + AdditionalText SonarLintXml, + ImmutableDictionary DiagnosticOptions, + ImmutableArray Analyzers); diff --git a/src/RoslynAnalyzerServer/Analysis/SonarRoslynDiagnosticsConverter.cs b/src/RoslynAnalyzerServer/Analysis/SonarRoslynDiagnosticsConverter.cs new file mode 100644 index 000000000..c6ab30490 --- /dev/null +++ b/src/RoslynAnalyzerServer/Analysis/SonarRoslynDiagnosticsConverter.cs @@ -0,0 +1,57 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2025 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using System.ComponentModel.Composition; +using Microsoft.CodeAnalysis; +using SonarLint.VisualStudio.Core; + +namespace SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis; + +public interface IRoslynDiagnosticsConverter +{ + SonarDiagnostic ConvertToSonarDiagnostic(Diagnostic diagnostic, Language language); +} + +[Export(typeof(IRoslynDiagnosticsConverter))] +[PartCreationPolicy(CreationPolicy.Shared)] +public class SonarRoslynDiagnosticsConverter : IRoslynDiagnosticsConverter +{ + public SonarDiagnostic ConvertToSonarDiagnostic(Diagnostic diagnostic, Language language) + { + var fileLinePositionSpan = diagnostic.Location.GetMappedLineSpan(); + + var textRange = new SonarTextRange( + fileLinePositionSpan.StartLinePosition.Line + 1, + fileLinePositionSpan.EndLinePosition.Line + 1, + fileLinePositionSpan.StartLinePosition.Character, + fileLinePositionSpan.EndLinePosition.Character); + + var location = new SonarDiagnosticLocation( + diagnostic.GetMessage(), + fileLinePositionSpan.Path, + textRange); + + // todo SLVS-2427 quick fixes + // todo SLVS-2428 secondary locations + return new SonarDiagnostic( + new SonarCompositeRuleId(language.RepoInfo.Key, diagnostic.Id).ErrorListErrorCode, + location); + } +} diff --git a/src/RoslynAnalyzerServer/Analysis/SonarRoslynFileSemanticAnalysis.cs b/src/RoslynAnalyzerServer/Analysis/SonarRoslynFileSemanticAnalysis.cs new file mode 100644 index 000000000..165aa5ea4 --- /dev/null +++ b/src/RoslynAnalyzerServer/Analysis/SonarRoslynFileSemanticAnalysis.cs @@ -0,0 +1,43 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2025 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using SonarLint.VisualStudio.Core; +using SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis.Wrappers; + +namespace SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis; + +internal class SonarRoslynFileSemanticAnalysis(string analysisFilePath, ILogger logger) : ISonarRoslynAnalysisCommand +{ + public string AnalysisFilePath { get; } = analysisFilePath; + + public async Task> ExecuteAsync(ISonarRoslynCompilationWithAnalyzersWrapper compilation, CancellationToken token) + { + var semanticModel = compilation.GetSemanticModel(AnalysisFilePath); + if (semanticModel == null) + { + logger.LogVerbose("No semantic model found for {0}", AnalysisFilePath); + return ImmutableArray.Empty; + } + + return await compilation.GetAnalyzerSemanticDiagnosticsAsync(semanticModel, token); + } +} diff --git a/src/RoslynAnalyzerServer/Analysis/SonarRoslynFileSyntaxAnalysis.cs b/src/RoslynAnalyzerServer/Analysis/SonarRoslynFileSyntaxAnalysis.cs new file mode 100644 index 000000000..daa722eb5 --- /dev/null +++ b/src/RoslynAnalyzerServer/Analysis/SonarRoslynFileSyntaxAnalysis.cs @@ -0,0 +1,43 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2025 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using SonarLint.VisualStudio.Core; +using SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis.Wrappers; + +namespace SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis; + +internal class SonarRoslynFileSyntaxAnalysis(string analysisFilePath, ILogger logger) : ISonarRoslynAnalysisCommand +{ + public string AnalysisFilePath { get; } = analysisFilePath; + + public async Task> ExecuteAsync(ISonarRoslynCompilationWithAnalyzersWrapper compilation, CancellationToken token) + { + var syntaxTree = compilation.GetSyntaxTree(AnalysisFilePath); + if (syntaxTree == null) + { + logger.LogVerbose("No syntax tree found for {0}", AnalysisFilePath); + return ImmutableArray.Empty; + } + + return await compilation.GetAnalyzerSyntaxDiagnosticsAsync(syntaxTree, token); + } +} diff --git a/src/RoslynAnalyzerServer/Analysis/SonarRoslynProjectAnalysisSet.cs b/src/RoslynAnalyzerServer/Analysis/SonarRoslynProjectAnalysisSet.cs new file mode 100644 index 000000000..3aa1fff94 --- /dev/null +++ b/src/RoslynAnalyzerServer/Analysis/SonarRoslynProjectAnalysisSet.cs @@ -0,0 +1,29 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2025 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis.Wrappers; + +namespace SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis; + +internal class SonarRoslynProjectAnalysisSet(ISonarRoslynProjectWrapper project, IReadOnlyCollection analysisCommands) +{ + public ISonarRoslynProjectWrapper Project { get; } = project; + public IReadOnlyCollection AnalysisCommands { get; } = analysisCommands; +} diff --git a/src/RoslynAnalyzerServer/Analysis/SonarRoslynProjectCompilationProvider.cs b/src/RoslynAnalyzerServer/Analysis/SonarRoslynProjectCompilationProvider.cs new file mode 100644 index 000000000..4e4772d83 --- /dev/null +++ b/src/RoslynAnalyzerServer/Analysis/SonarRoslynProjectCompilationProvider.cs @@ -0,0 +1,88 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2025 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using System.Collections.Immutable; +using System.ComponentModel.Composition; +using System.IO; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using SonarLint.VisualStudio.Core; +using SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis.Wrappers; + +namespace SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis; + +[Export(typeof(ISonarRoslynProjectCompilationProvider))] +[PartCreationPolicy(CreationPolicy.Shared)] +[method: ImportingConstructor] +internal class SonarRoslynProjectCompilationProvider(ILogger logger) : ISonarRoslynProjectCompilationProvider +{ + public async Task GetProjectCompilationAsync( + ISonarRoslynProjectWrapper project, + ImmutableDictionary sonarRoslynAnalysisConfigurations, + CancellationToken token) + { + var compilation = await project.GetCompilationAsync(token); + + var analysisConfigurationForLanguage = sonarRoslynAnalysisConfigurations[compilation.Language]; + + return ApplyAnalyzersAndAdditionalFile( + ApplyDiagnosticOptions(compilation, analysisConfigurationForLanguage), + project, + analysisConfigurationForLanguage); + } + + private ISonarRoslynCompilationWithAnalyzersWrapper ApplyAnalyzersAndAdditionalFile( + ISonarRoslynCompilationWrapper compilation, + ISonarRoslynProjectWrapper project, + SonarRoslynAnalysisConfiguration analysisConfigurationForLanguage) + { + var additionalFiles = project.RoslynAnalyzerOptions.AdditionalFiles; + var sonarLintXmlName = Path.GetFileName(analysisConfigurationForLanguage.SonarLintXml.Path); + var analyzerOptions = project.RoslynAnalyzerOptions.WithAdditionalFiles(additionalFiles + .Where(x => Path.GetFileName(x.Path) != sonarLintXmlName) + .Concat([analysisConfigurationForLanguage.SonarLintXml]) + .ToImmutableArray()); + + var compilationWithAnalyzersOptions = new CompilationWithAnalyzersOptions( + analyzerOptions, + OnAnalyzerException, + true, + false, + false); + + return compilation + .WithAnalyzers(analysisConfigurationForLanguage.Analyzers, compilationWithAnalyzersOptions); + } + + private static ISonarRoslynCompilationWrapper ApplyDiagnosticOptions( + ISonarRoslynCompilationWrapper compilation, + SonarRoslynAnalysisConfiguration analysisConfigurationForLanguage) + { + var compilationOptions = compilation.RoslynCompilationOptions.WithSpecificDiagnosticOptions(analysisConfigurationForLanguage.DiagnosticOptions); + compilation = compilation + .WithOptions(compilationOptions); + return compilation; + } + + private void OnAnalyzerException(Exception arg1, DiagnosticAnalyzer arg2, Diagnostic arg3) => + logger.LogVerbose( + new MessageLevelContext { Context = ["Roslyn Analyzer Exception", arg2.GetType().Name, arg3.Id] }, + arg1.ToString()); +} diff --git a/src/RoslynAnalyzerServer/Analysis/SonarRoslynSolutionAnalysisCommandProvider.cs b/src/RoslynAnalyzerServer/Analysis/SonarRoslynSolutionAnalysisCommandProvider.cs new file mode 100644 index 000000000..ce96cf74d --- /dev/null +++ b/src/RoslynAnalyzerServer/Analysis/SonarRoslynSolutionAnalysisCommandProvider.cs @@ -0,0 +1,86 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2025 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using System.ComponentModel.Composition; +using SonarLint.VisualStudio.Core; +using SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis.Wrappers; + +namespace SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis; + +[Export(typeof(ISonarRoslynSolutionAnalysisCommandProvider))] +[PartCreationPolicy(CreationPolicy.Shared)] +[method: ImportingConstructor] +internal class SonarRoslynSolutionAnalysisCommandProvider( + ISonarRoslynWorkspaceWrapper roslynWorkspaceWrapper, + ILogger logger) : ISonarRoslynSolutionAnalysisCommandProvider +{ + private readonly ILogger logger = logger.ForContext("Roslyn Analysis", "Configuration"); + + public List GetAnalysisCommandsForCurrentSolution(string[] filePaths) + { + var result = new List(); + + var solution = roslynWorkspaceWrapper.CurrentSolution; + + foreach (var project in solution.Projects) + { + if (!project.SupportsCompilation) + { + logger.LogVerbose("Failed to get compilation for project: {0}", project.Name); + continue; + } + + var commands = GetCompilationCommandsForProject(filePaths, project); + + if (commands.Any()) + { + result.Add(new SonarRoslynProjectAnalysisSet(project, commands)); + } + else + { + logger.LogVerbose("No files to analyze in project {0}", project.Name); + } + } + + if (!result.Any()) + { + logger.WriteLine("No projects to analyze"); + } + + return result; + } + + private List GetCompilationCommandsForProject(string[] filePaths, ISonarRoslynProjectWrapper project) + { + var commands = new List(); + + foreach (var filePath in filePaths) + { + if (!project.ContainsDocument(filePath, out var analysisFilePath)) + { + continue; + } + + commands.Add(new SonarRoslynFileSyntaxAnalysis(analysisFilePath, logger)); + commands.Add(new SonarRoslynFileSemanticAnalysis(analysisFilePath, logger)); + } + return commands; + } +} diff --git a/src/RoslynAnalyzerServer/Analysis/Wrappers/ISonarRoslynCompilationWithAnalyzersWrapper.cs b/src/RoslynAnalyzerServer/Analysis/Wrappers/ISonarRoslynCompilationWithAnalyzersWrapper.cs new file mode 100644 index 000000000..26433bb87 --- /dev/null +++ b/src/RoslynAnalyzerServer/Analysis/Wrappers/ISonarRoslynCompilationWithAnalyzersWrapper.cs @@ -0,0 +1,36 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2025 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using SonarLint.VisualStudio.Core; + +namespace SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis.Wrappers; + +public interface ISonarRoslynCompilationWithAnalyzersWrapper +{ + Language Language { get; } + SyntaxTree? GetSyntaxTree(string filePath); + + SemanticModel? GetSemanticModel(string filePath); + + Task> GetAnalyzerSyntaxDiagnosticsAsync(SyntaxTree syntaxTree, CancellationToken token); + Task> GetAnalyzerSemanticDiagnosticsAsync(SemanticModel semanticModel, CancellationToken token); +} diff --git a/src/RoslynAnalyzerServer/Analysis/Wrappers/ISonarRoslynCompilationWrapper.cs b/src/RoslynAnalyzerServer/Analysis/Wrappers/ISonarRoslynCompilationWrapper.cs new file mode 100644 index 000000000..069106346 --- /dev/null +++ b/src/RoslynAnalyzerServer/Analysis/Wrappers/ISonarRoslynCompilationWrapper.cs @@ -0,0 +1,36 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2025 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using SonarLint.VisualStudio.Core; + +namespace SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis.Wrappers; + +internal interface ISonarRoslynCompilationWrapper +{ + CompilationOptions RoslynCompilationOptions { get; } + Language Language { get; } + + ISonarRoslynCompilationWrapper WithOptions(CompilationOptions withSpecificDiagnosticOptions); + + ISonarRoslynCompilationWithAnalyzersWrapper WithAnalyzers(ImmutableArray analyzers, CompilationWithAnalyzersOptions compilationWithAnalyzersOptions); +} diff --git a/src/RoslynAnalyzerServer/Analysis/Wrappers/ISonarRoslynProjectWrapper.cs b/src/RoslynAnalyzerServer/Analysis/Wrappers/ISonarRoslynProjectWrapper.cs new file mode 100644 index 000000000..444598dc8 --- /dev/null +++ b/src/RoslynAnalyzerServer/Analysis/Wrappers/ISonarRoslynProjectWrapper.cs @@ -0,0 +1,39 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2025 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis.Wrappers; + +internal interface ISonarRoslynProjectWrapper +{ + string Name { get; } + + bool SupportsCompilation { get; } + + AnalyzerOptions RoslynAnalyzerOptions { get; } + + bool ContainsDocument( + string filePath, + [NotNullWhen(true)]out string? analysisFilePath); + + Task GetCompilationAsync(CancellationToken token); +} diff --git a/src/RoslynAnalyzerServer/Analysis/Wrappers/ISonarRoslynSolutionWrapper.cs b/src/RoslynAnalyzerServer/Analysis/Wrappers/ISonarRoslynSolutionWrapper.cs new file mode 100644 index 000000000..6b1c30bb3 --- /dev/null +++ b/src/RoslynAnalyzerServer/Analysis/Wrappers/ISonarRoslynSolutionWrapper.cs @@ -0,0 +1,26 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2025 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +namespace SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis.Wrappers; + +internal interface ISonarRoslynSolutionWrapper +{ + public IEnumerable Projects { get; } +} diff --git a/src/RoslynAnalyzerServer/Analysis/Wrappers/ISonarRoslynWorkspaceWrapper.cs b/src/RoslynAnalyzerServer/Analysis/Wrappers/ISonarRoslynWorkspaceWrapper.cs new file mode 100644 index 000000000..e67065bbb --- /dev/null +++ b/src/RoslynAnalyzerServer/Analysis/Wrappers/ISonarRoslynWorkspaceWrapper.cs @@ -0,0 +1,26 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2025 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +namespace SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis.Wrappers; + +internal interface ISonarRoslynWorkspaceWrapper +{ + ISonarRoslynSolutionWrapper CurrentSolution { get; } +} diff --git a/src/RoslynAnalyzerServer/Analysis/Wrappers/SonarRoslynCompilationWithAnalyzersWrapper.cs b/src/RoslynAnalyzerServer/Analysis/Wrappers/SonarRoslynCompilationWithAnalyzersWrapper.cs new file mode 100644 index 000000000..ff672cb21 --- /dev/null +++ b/src/RoslynAnalyzerServer/Analysis/Wrappers/SonarRoslynCompilationWithAnalyzersWrapper.cs @@ -0,0 +1,43 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2025 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using SonarLint.VisualStudio.Core; + +namespace SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis.Wrappers; + +[ExcludeFromCodeCoverage] // todo SLVS-2466 add roslyn 'integration' tests using AdHocWorkspace +public class SonarRoslynCompilationWithAnalyzersWrapper(CompilationWithAnalyzers compilation, Language language) : ISonarRoslynCompilationWithAnalyzersWrapper +{ + public Language Language { get; } = language; + + public SyntaxTree? GetSyntaxTree(string filePath) => compilation.Compilation.SyntaxTrees.SingleOrDefault(x => filePath.Equals(x.FilePath)); + + public SemanticModel? GetSemanticModel(string filePath) => GetSyntaxTree(filePath) is {} syntaxTree ? compilation.Compilation.GetSemanticModel(syntaxTree) : null; + + public Task> GetAnalyzerSyntaxDiagnosticsAsync(SyntaxTree syntaxTree, CancellationToken token) => + compilation.GetAnalyzerSyntaxDiagnosticsAsync(syntaxTree, token); + + public Task> GetAnalyzerSemanticDiagnosticsAsync(SemanticModel semanticModel, CancellationToken token) => + compilation.GetAnalyzerSemanticDiagnosticsAsync(semanticModel, null, token); +} diff --git a/src/RoslynAnalyzerServer/Analysis/Wrappers/SonarRoslynCompilationWrapper.cs b/src/RoslynAnalyzerServer/Analysis/Wrappers/SonarRoslynCompilationWrapper.cs new file mode 100644 index 000000000..2a6e7c601 --- /dev/null +++ b/src/RoslynAnalyzerServer/Analysis/Wrappers/SonarRoslynCompilationWrapper.cs @@ -0,0 +1,45 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2025 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using SonarLint.VisualStudio.Core; + +namespace SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis.Wrappers; + +[ExcludeFromCodeCoverage] // todo SLVS-2466 add roslyn 'integration' tests using AdHocWorkspace +internal class SonarRoslynCompilationWrapper(Compilation roslynCompilation) : ISonarRoslynCompilationWrapper +{ + public CompilationOptions RoslynCompilationOptions => roslynCompilation.Options; + public Language Language { get; } = roslynCompilation.Language switch + { + LanguageNames.CSharp => Language.CSharp, + LanguageNames.VisualBasic => Language.VBNET, + _ => throw new ArgumentOutOfRangeException(nameof(roslynCompilation)), + }; + + public ISonarRoslynCompilationWrapper WithOptions(CompilationOptions withSpecificDiagnosticOptions) => + new SonarRoslynCompilationWrapper(roslynCompilation.WithOptions(withSpecificDiagnosticOptions)); + + public ISonarRoslynCompilationWithAnalyzersWrapper WithAnalyzers(ImmutableArray analyzers, CompilationWithAnalyzersOptions compilationWithAnalyzersOptions) => + new SonarRoslynCompilationWithAnalyzersWrapper(roslynCompilation.WithAnalyzers(analyzers, compilationWithAnalyzersOptions), Language); +} diff --git a/src/RoslynAnalyzerServer/Analysis/Wrappers/SonarRoslynProjectWrapper.cs b/src/RoslynAnalyzerServer/Analysis/Wrappers/SonarRoslynProjectWrapper.cs new file mode 100644 index 000000000..8288ce6ee --- /dev/null +++ b/src/RoslynAnalyzerServer/Analysis/Wrappers/SonarRoslynProjectWrapper.cs @@ -0,0 +1,52 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2025 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis.Wrappers; + +[ExcludeFromCodeCoverage] // todo SLVS-2466 add roslyn 'integration' tests using AdHocWorkspace +internal class SonarRoslynProjectWrapper(Project project) : ISonarRoslynProjectWrapper +{ + public string Name => project.Name; + public bool SupportsCompilation => project.SupportsCompilation; + public AnalyzerOptions RoslynAnalyzerOptions => project.AnalyzerOptions; + + public async Task GetCompilationAsync(CancellationToken token) => + new SonarRoslynCompilationWrapper((await project.GetCompilationAsync(token))!); + + public bool ContainsDocument( + string filePath, + [NotNullWhen(true)]out string? analysisFilePath) + { + analysisFilePath = project.Documents + .Select(document => document.FilePath) + .Where(path => path != null) + .FirstOrDefault(path => + path!.Equals(filePath) + || (path.StartsWith(filePath) + && path.EndsWith(".g.cs"))); + + return analysisFilePath != null; + } + +} diff --git a/src/RoslynAnalyzerServer/Analysis/Wrappers/SonarRoslynSolutionWrapper.cs b/src/RoslynAnalyzerServer/Analysis/Wrappers/SonarRoslynSolutionWrapper.cs new file mode 100644 index 000000000..704beb9b7 --- /dev/null +++ b/src/RoslynAnalyzerServer/Analysis/Wrappers/SonarRoslynSolutionWrapper.cs @@ -0,0 +1,30 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2025 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis; + +namespace SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis.Wrappers; + +[ExcludeFromCodeCoverage] // todo SLVS-2466 add roslyn 'integration' tests using AdHocWorkspace +internal class SonarRoslynSolutionWrapper(Solution workspaceCurrentSolution) : ISonarRoslynSolutionWrapper +{ + public IEnumerable Projects { get; } = workspaceCurrentSolution.Projects.Select(x => new SonarRoslynProjectWrapper(x)); +} diff --git a/src/RoslynAnalyzerServer/Analysis/Wrappers/SonarRoslynWorkspaceWrapper.cs b/src/RoslynAnalyzerServer/Analysis/Wrappers/SonarRoslynWorkspaceWrapper.cs new file mode 100644 index 000000000..8c904de16 --- /dev/null +++ b/src/RoslynAnalyzerServer/Analysis/Wrappers/SonarRoslynWorkspaceWrapper.cs @@ -0,0 +1,35 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2025 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using System.ComponentModel.Composition; +using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis; +using Microsoft.VisualStudio.LanguageServices; + +namespace SonarLint.VisualStudio.RoslynAnalyzerServer.Analysis.Wrappers; + +[ExcludeFromCodeCoverage] // todo SLVS-2466 add roslyn 'integration' tests using AdHocWorkspace +[Export(typeof(ISonarRoslynWorkspaceWrapper))] +[PartCreationPolicy(CreationPolicy.Shared)] +[method: ImportingConstructor] +internal class SonarRoslynWorkspaceWrapper([Import(typeof(VisualStudioWorkspace))] Workspace workspace) : ISonarRoslynWorkspaceWrapper +{ + public ISonarRoslynSolutionWrapper CurrentSolution => new SonarRoslynSolutionWrapper(workspace.CurrentSolution); +} diff --git a/src/RoslynAnalyzerServer/RoslynAnalyzerServer.csproj b/src/RoslynAnalyzerServer/RoslynAnalyzerServer.csproj index f82cc646d..187ce6bb0 100644 --- a/src/RoslynAnalyzerServer/RoslynAnalyzerServer.csproj +++ b/src/RoslynAnalyzerServer/RoslynAnalyzerServer.csproj @@ -1,6 +1,8 @@  - + + + SonarLint.VisualStudio.RoslynAnalyzerServer diff --git a/src/RoslynAnalyzerServer/packages.lock.json b/src/RoslynAnalyzerServer/packages.lock.json index f23c2607b..30432a6f4 100644 --- a/src/RoslynAnalyzerServer/packages.lock.json +++ b/src/RoslynAnalyzerServer/packages.lock.json @@ -30,6 +30,21 @@ "System.IO.Pipelines": "5.0.1" } }, + "Microsoft.VisualStudio.LanguageServices": { + "type": "Direct", + "requested": "[3.11.0, )", + "resolved": "3.11.0", + "contentHash": "igs65alqrom0ogrF/ehqc3g1bmekoZERDLHwIOcZDvruRFwhmTo3rBhOTiKEAUChg62aPWIvWB1mVYAgda1tMQ==", + "dependencies": { + "Microsoft.CSharp": "4.3.0", + "Microsoft.CodeAnalysis.Common": "[3.11.0]", + "Microsoft.CodeAnalysis.EditorFeatures.Text": "[3.11.0]", + "Microsoft.CodeAnalysis.Features": "[3.11.0]", + "Microsoft.CodeAnalysis.Workspaces.Common": "[3.11.0]", + "Microsoft.VisualStudio.Composition": "16.9.20", + "System.Threading.Tasks.Dataflow": "5.0.0" + } + }, "BouncyCastle.Cryptography": { "type": "Transitive", "resolved": "2.4.0", @@ -40,6 +55,30 @@ "resolved": "2.2.0", "contentHash": "rsYXB7+iUPP8AHgQ8JP2UZI2xK2KhjcdGr9E6zX3CsZaTLCaw8M35vaAJRo1rfxeaZEVMuXeaquLVCkZ7JcZ5Q==" }, + "MessagePack": { + "type": "Transitive", + "resolved": "2.1.152", + "contentHash": "PlJ31qf42uGuJfwc61x/Pt4hJi01xh1rrBofj1MJSLzEot/2UAIRdSgxEHN/8qou5CV8OBeDM9HXKPi1Oj8rpQ==", + "dependencies": { + "MessagePack.Annotations": "2.1.152", + "Microsoft.Bcl.AsyncInterfaces": "1.0.0", + "System.Memory": "4.5.3", + "System.Reflection.Emit": "4.6.0", + "System.Reflection.Emit.Lightweight": "4.6.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.2", + "System.Threading.Tasks.Extensions": "4.5.3" + } + }, + "MessagePack.Annotations": { + "type": "Transitive", + "resolved": "2.1.152", + "contentHash": "RONktDA/HA641ds/2bfOqYSVew8o8EJMcQ1P4M1J77QGgbzWiWt3nBHvCAwlx0VfO6K9S8xq4b5OLD2CUnhtCg==" + }, + "MessagePackAnalyzer": { + "type": "Transitive", + "resolved": "2.1.152", + "contentHash": "uJhZlGMkXDaFYsH8V9S6o1EyvsUqB9mpU4DVBXNr0DXZVzZMhuLP1IkLj5xK3EKlaAcvkFkZv3eSvuz360wb3Q==" + }, "Microsoft.Bcl.AsyncInterfaces": { "type": "Transitive", "resolved": "5.0.0", @@ -53,6 +92,157 @@ "resolved": "3.3.2", "contentHash": "7xt6zTlIEizUgEsYAIgm37EbdkiMmr6fP6J9pDoKEpiGM4pi32BCPGr/IczmSJI9Zzp0a6HOzpr9OvpMP+2veA==" }, + "Microsoft.CodeAnalysis.AnalyzerUtilities": { + "type": "Transitive", + "resolved": "3.3.0", + "contentHash": "gyQ70pJ4T7hu/s0+QnEaXtYfeG/JrttGnxHJlrhpxsQjRIUGuRhVwNBtkHHYOrUAZ/l47L98/NiJX6QmTwAyrg==" + }, + "Microsoft.CodeAnalysis.EditorFeatures.Text": { + "type": "Transitive", + "resolved": "3.11.0", + "contentHash": "kzTA3dFcBB4M3ft/T4nSPi4fst4N7Vi37zUnWhhs0SAb/jEqeWvD+g4RkB4/0x2pg6YlaaK7ZXqosUqiizhGNw==", + "dependencies": { + "Microsoft.CodeAnalysis.Common": "[3.11.0]", + "Microsoft.CodeAnalysis.Workspaces.Common": "[3.11.0]", + "Microsoft.VisualStudio.CoreUtility": "16.10.230", + "Microsoft.VisualStudio.Text.Data": "16.10.230", + "Microsoft.VisualStudio.Text.Logic": "16.10.230", + "Microsoft.VisualStudio.Threading": "16.10.56" + } + }, + "Microsoft.CodeAnalysis.Features": { + "type": "Transitive", + "resolved": "3.11.0", + "contentHash": "FZi4btbqla9fxBbMBOrlwJIvOxmb80E32fQ69IhU4NNQiLSF3LIyOUSQvnV6xl1MccGET7W+NUPtEexsH8GNjA==", + "dependencies": { + "Microsoft.CodeAnalysis.AnalyzerUtilities": "3.3.0", + "Microsoft.CodeAnalysis.Common": "[3.11.0]", + "Microsoft.CodeAnalysis.Scripting.Common": "[3.11.0]", + "Microsoft.CodeAnalysis.Workspaces.Common": "[3.11.0]", + "Microsoft.DiaSymReader": "1.3.0", + "Microsoft.VisualStudio.Debugger.Contracts": "16.10.0-beta.21214.1", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.CodeAnalysis.Scripting.Common": { + "type": "Transitive", + "resolved": "3.11.0", + "contentHash": "3R0TwSj+74/iDl9R2MDwsGiyrYjCnIKs5a1e17Fa/jGI/C5/c3v/X7tAhnvdXl7pIUnXtTjyWrkVLTv5EKBPmQ==", + "dependencies": { + "Microsoft.CodeAnalysis.Common": "[3.11.0]" + } + }, + "Microsoft.CSharp": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "P+MBhIM0YX+JqROuf7i306ZLJEjQYA9uUyRDE+OqwUI5sh41e2ZbPQV3LfAPh+29cmceE1pUffXsGfR4eMY3KA==" + }, + "Microsoft.DiaSymReader": { + "type": "Transitive", + "resolved": "1.3.0", + "contentHash": "/fn1Tfo7j7k/slViPlM8azJuxQmri7FZ8dQ+gTeLbI29leN/1VK0U/BFcRdJNctsRCUgyKJ2q+I0Tjq07Rc1/Q==" + }, + "Microsoft.VisualStudio.Composition": { + "type": "Transitive", + "resolved": "16.9.20", + "contentHash": "7vrGl8+9p+vBpqfnQlQ0I2/2qRk3coYxszn9Mu12Xxxoqc/FfHcxJUyGgdqoc0o1EAwkXzKm7FNuyOHFhQ1McQ==", + "dependencies": { + "Microsoft.VisualStudio.Composition.Analyzers": "16.9.20", + "Microsoft.VisualStudio.Composition.NetFxAttributes": "16.9.20", + "Microsoft.VisualStudio.Validation": "15.5.31", + "System.ComponentModel.Composition": "4.5.0", + "System.Composition": "1.0.31", + "System.Reflection.Emit": "4.3.0", + "System.Reflection.Metadata": "1.6.0", + "System.Reflection.TypeExtensions": "4.3.0", + "System.Threading.Tasks.Dataflow": "4.11.1" + } + }, + "Microsoft.VisualStudio.Composition.Analyzers": { + "type": "Transitive", + "resolved": "16.9.20", + "contentHash": "3SoUdxqmeczGmNwifgzlVDMtSYd3tyB/9qoqiN8dl9UVPI3uQKW2XwUQn86l34P3IabJMsj4EbHKZ8q9fkeDGw==" + }, + "Microsoft.VisualStudio.Composition.NetFxAttributes": { + "type": "Transitive", + "resolved": "16.9.20", + "contentHash": "HlPvzs95ONvqML2WsEyu9WpBSMYTwrKugeyyRXeH0x6qDVfYhkSVbZz9nLXJQstNMY+OY7aLu4xKZOwui9f6ag==", + "dependencies": { + "System.ComponentModel.Composition": "4.5.0" + } + }, + "Microsoft.VisualStudio.CoreUtility": { + "type": "Transitive", + "resolved": "16.10.230", + "contentHash": "segnoghs4hfsD3tpM8t+6gcP1PS4a8iHR2GfeAxQjuNQPNnbFSdB376O0wjAx5/qk8H4ZyZJ7fDfv0iP0ZTL/Q==", + "dependencies": { + "Microsoft.VisualStudio.Threading": "16.10.56", + "System.Collections.Immutable": "5.0.0", + "System.ComponentModel.Composition": "4.5.0" + } + }, + "Microsoft.VisualStudio.Debugger.Contracts": { + "type": "Transitive", + "resolved": "16.10.0-beta.21214.1", + "contentHash": "ybpSfj18yDue4O4YAljlkHrCFECjTAy/llhlDFE2NO5e586zdsjfqwJw5+ydVM2WCYQK+CwlB6YboKaqjnKGjA==", + "dependencies": { + "MessagePack": "2.1.152", + "MessagePackAnalyzer": "2.1.152", + "Microsoft.Bcl.AsyncInterfaces": "1.1.1", + "System.Collections.Immutable": "1.5.0" + } + }, + "Microsoft.VisualStudio.Text.Data": { + "type": "Transitive", + "resolved": "16.10.230", + "contentHash": "6uKWOu5nlP5PyxFj8brV2n2k0OY9Ax1/JVgY6QEdhcDMHrCEhIyAiZO4SsDoMmTSv0rXfgxLTZpl/+nQjPyfEg==", + "dependencies": { + "Microsoft.VisualStudio.CoreUtility": "16.10.230", + "Microsoft.VisualStudio.Threading": "16.10.56" + } + }, + "Microsoft.VisualStudio.Text.Logic": { + "type": "Transitive", + "resolved": "16.10.230", + "contentHash": "+eQJ9gkbZfr/pFBJxjHNND9X4e2vjK7AByacbB2+vNen85dXhpwxvZnKYNlnbubtROwmc8CIPSaj+Kg3xHc9kA==", + "dependencies": { + "Microsoft.VisualStudio.CoreUtility": "16.10.230", + "Microsoft.VisualStudio.Text.Data": "16.10.230", + "System.Collections.Immutable": "5.0.0", + "System.ComponentModel.Composition": "4.5.0" + } + }, + "Microsoft.VisualStudio.Threading": { + "type": "Transitive", + "resolved": "16.10.56", + "contentHash": "RjV4Y+/Qh+nSFVPJJXby56W4Th5Z4fi8dBfJY5v8HsO7vA749OsqGdVVfUiXAySsZmJTh6cE9kM0faB/dgIPFA==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "Microsoft.VisualStudio.Threading.Analyzers": "16.10.56", + "Microsoft.VisualStudio.Validation": "16.9.32", + "Microsoft.Win32.Registry": "5.0.0", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.VisualStudio.Threading.Analyzers": { + "type": "Transitive", + "resolved": "16.10.56", + "contentHash": "uTvu9fbmHTtRFY5xUgG45waU+ARad0JxyG/2btOC9Su6RTo3PrGVXKU2vHDtVELqlJsnKImhMaPH3YR0XCVwDg==" + }, + "Microsoft.VisualStudio.Validation": { + "type": "Transitive", + "resolved": "16.9.32", + "contentHash": "4Ozqy5OjtU4k9aKT76or3y8tVq6ju8+ba4YZHIe5wTb28jeCg11zkK6bdI4kj8s+k5OSekyO+FkUyoOkvPkgVg==" + }, + "Microsoft.Win32.Registry": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, "Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.3", @@ -71,6 +261,11 @@ "System.Memory": "4.5.4" } }, + "System.ComponentModel.Composition": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "+iB9FoZnfdqMEGq6np28X6YNSUrse16CakmIhV3h6PxEWt7jYxUN3Txs1D8MZhhf4QmyvK0F/EcIN0f4gGN0dA==" + }, "System.Composition": { "type": "Transitive", "resolved": "1.0.31", @@ -149,6 +344,21 @@ "resolved": "4.5.0", "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" }, + "System.Reflection": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==" + }, + "System.Reflection.Emit": { + "type": "Transitive", + "resolved": "4.6.0", + "contentHash": "qAo4jyXtC9i71iElngX7P2r+zLaiHzxKwf66sc3X91tL5Ks6fnQ1vxL04o7ZSm3sYfLExySL7GN8aTpNYpU1qw==" + }, + "System.Reflection.Emit.Lightweight": { + "type": "Transitive", + "resolved": "4.6.0", + "contentHash": "j/V5HVvxvBQ7uubYD0PptQW2KGsi1Pc2kZ9yfwLixv3ADdjL/4M78KyC5e+ymW612DY8ZE4PFoZmWpoNmN2mqg==" + }, "System.Reflection.Metadata": { "type": "Transitive", "resolved": "5.0.0", @@ -157,11 +367,32 @@ "System.Collections.Immutable": "5.0.0" } }, + "System.Reflection.TypeExtensions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==", + "dependencies": { + "System.Reflection": "4.3.0" + } + }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", "resolved": "5.0.0", "contentHash": "ZD9TMpsmYJLrxbbmdvhwt9YEgG5WntEnZ/d1eH8JBX9LBp+Ju8BSBhUGbZMNVHHomWo2KVImJhTDl2hIgw/6MA==" }, + "System.Security.AccessControl": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "dependencies": { + "System.Security.Principal.Windows": "5.0.0" + } + }, + "System.Security.Principal.Windows": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" + }, "System.Text.Encoding.CodePages": { "type": "Transitive", "resolved": "4.5.1", @@ -178,6 +409,11 @@ "System.Threading.Tasks.Extensions": "4.5.4" } }, + "System.Threading.Tasks.Dataflow": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "NBp0zSAMZp4muDje6XmbDfmkqw9+qsDCHp+YMEtnVgHEjQZ3Q7MzFTTp3eHqpExn4BwMrS7JkUVOTcVchig4Sw==" + }, "System.Threading.Tasks.Extensions": { "type": "Transitive", "resolved": "4.5.4",