diff --git a/src/Infrastructure.VS.UnitTests/Roslyn/RoslynSolutionWrapperTests.cs b/src/Infrastructure.VS.UnitTests/Roslyn/RoslynSolutionWrapperTests.cs index 44291f8730..3c4532b871 100644 --- a/src/Infrastructure.VS.UnitTests/Roslyn/RoslynSolutionWrapperTests.cs +++ b/src/Infrastructure.VS.UnitTests/Roslyn/RoslynSolutionWrapperTests.cs @@ -41,10 +41,27 @@ public void AddAnalyzer_CurrentSolutionContainsAnalyzer() var roslynWorkspaceWrapper = CreateWorkspaceWrapper(); var analyzers = ImmutableArray.Create(analyzerFileReference); - var solutionAfterAddition = roslynWorkspaceWrapper.CurrentSolution.WithAnalyzerReferences(analyzers); + var solutionAfterAddition = roslynWorkspaceWrapper.CurrentSolution.AddAnalyzerReferences(analyzers); roslynWorkspaceWrapper.TryApplyChanges(solutionAfterAddition).Should().BeTrue(); - roslynWorkspaceWrapper.CurrentSolution.GetRoslynSolution().AnalyzerReferences.Contains(analyzerFileReference).Should().BeTrue(); + roslynWorkspaceWrapper.CurrentSolution.GetRoslynSolution().AnalyzerReferences.Should().Contain(analyzerFileReference); + } + + [TestMethod] + public void AddExtraAnalyzer_CurrentSolutionContainsBothAnalyzers() + { + var analyzerFileReference = new AnalyzerFileReference(@"C:\abc", Substitute.For()); + var extraAnalyzerFileReference = new AnalyzerFileReference(@"C:\abc", Substitute.For()); + var roslynWorkspaceWrapper = CreateWorkspaceWrapper(); + var extraAnalyzers = ImmutableArray.Create(extraAnalyzerFileReference); + roslynWorkspaceWrapper.TryApplyChanges(roslynWorkspaceWrapper.CurrentSolution.AddAnalyzerReferences(ImmutableArray.Create(analyzerFileReference))).Should().BeTrue(); + + var solutionAfterAddition = roslynWorkspaceWrapper.CurrentSolution.AddAnalyzerReferences(extraAnalyzers); + roslynWorkspaceWrapper.TryApplyChanges(solutionAfterAddition).Should().BeTrue(); + + var solutionAnalyzers = roslynWorkspaceWrapper.CurrentSolution.GetRoslynSolution().AnalyzerReferences; + solutionAnalyzers.Should().Contain(analyzerFileReference); + solutionAnalyzers.Should().Contain(extraAnalyzerFileReference); } [TestMethod] @@ -54,13 +71,13 @@ public void AddAndRemoveAnalyzer_CurrentSolutionNoLongerContainsAnalyzer() var roslynWorkspaceWrapper = CreateWorkspaceWrapper(); var analyzers = ImmutableArray.Create(analyzerFileReference); - var solutionAfterAddition = roslynWorkspaceWrapper.CurrentSolution.WithAnalyzerReferences(analyzers); + var solutionAfterAddition = roslynWorkspaceWrapper.CurrentSolution.AddAnalyzerReferences(analyzers); roslynWorkspaceWrapper.TryApplyChanges(solutionAfterAddition).Should().BeTrue(); var solutionAfterRemoval = roslynWorkspaceWrapper.CurrentSolution.RemoveAnalyzerReferences(analyzers); roslynWorkspaceWrapper.TryApplyChanges(solutionAfterRemoval).Should().BeTrue(); - roslynWorkspaceWrapper.CurrentSolution.GetRoslynSolution().AnalyzerReferences.Contains(analyzerFileReference).Should().BeFalse(); + roslynWorkspaceWrapper.CurrentSolution.GetRoslynSolution().AnalyzerReferences.Should().NotContain(analyzerFileReference); } [TestMethod] @@ -73,7 +90,7 @@ public void RemoveAnalyzer_IsNotPresentInTheCurrentSolution_AppliesNoChange() var solutionAfterRemoval = roslynWorkspaceWrapper.CurrentSolution.RemoveAnalyzerReferences(analyzers); roslynWorkspaceWrapper.TryApplyChanges(solutionAfterRemoval).Should().BeTrue(); - roslynWorkspaceWrapper.CurrentSolution.GetRoslynSolution().AnalyzerReferences.Contains(analyzerFileReference).Should().BeFalse(); + roslynWorkspaceWrapper.CurrentSolution.GetRoslynSolution().AnalyzerReferences.Should().NotContain(analyzerFileReference); } [TestMethod] @@ -85,10 +102,10 @@ public void AddMultipleAndRemoveOneAnalyzer_CurrentSolutionContainsOneAnalyzer() var analyzersToRemove = ImmutableArray.Create(analyzerFileReference1); var roslynWorkspaceWrapper = CreateWorkspaceWrapper(); - roslynWorkspaceWrapper.TryApplyChanges(roslynWorkspaceWrapper.CurrentSolution.WithAnalyzerReferences(analyzersToAdd)).Should().BeTrue(); + roslynWorkspaceWrapper.TryApplyChanges(roslynWorkspaceWrapper.CurrentSolution.AddAnalyzerReferences(analyzersToAdd)).Should().BeTrue(); roslynWorkspaceWrapper.TryApplyChanges(roslynWorkspaceWrapper.CurrentSolution.RemoveAnalyzerReferences(analyzersToRemove)).Should().BeTrue(); - roslynWorkspaceWrapper.CurrentSolution.GetRoslynSolution().AnalyzerReferences.Contains(analyzerFileReference1).Should().BeFalse(); - roslynWorkspaceWrapper.CurrentSolution.GetRoslynSolution().AnalyzerReferences.Contains(analyzerFileReference2).Should().BeTrue(); + roslynWorkspaceWrapper.CurrentSolution.GetRoslynSolution().AnalyzerReferences.Should().NotContain(analyzerFileReference1); + roslynWorkspaceWrapper.CurrentSolution.GetRoslynSolution().AnalyzerReferences.Should().Contain(analyzerFileReference2); } private static IRoslynWorkspaceWrapper CreateWorkspaceWrapper() diff --git a/src/Infrastructure.VS.UnitTests/Roslyn/SolutionRoslynAnalyzerManagerTests.cs b/src/Infrastructure.VS.UnitTests/Roslyn/SolutionRoslynAnalyzerManagerTests.cs index 5dc2d526e4..74c7c7cc49 100644 --- a/src/Infrastructure.VS.UnitTests/Roslyn/SolutionRoslynAnalyzerManagerTests.cs +++ b/src/Infrastructure.VS.UnitTests/Roslyn/SolutionRoslynAnalyzerManagerTests.cs @@ -130,7 +130,7 @@ public async Task OnSolutionStateChangedAsync_StandaloneSolution_BindingSet_Remo analyzerComparer.Equals(embeddedAnalyzers, connectedAnalyzers); v1Solution.RemoveAnalyzerReferences(embeddedAnalyzers); roslynWorkspaceWrapper.TryApplyChanges(v2Solution); - v2Solution.WithAnalyzerReferences(connectedAnalyzers); + v2Solution.AddAnalyzerReferences(connectedAnalyzers); roslynWorkspaceWrapper.TryApplyChanges(v3Solution); }); embeddedRoslynAnalyzerProvider.DidNotReceiveWithAnyArgs().Get(); @@ -172,7 +172,7 @@ public async Task OnSolutionStateChangedAsync_SolutionClosedAndReopened_Register await testSubject.OnSolutionStateChangedAsync(null); await testSubject.OnSolutionStateChangedAsync(solutionName); - v2Solution.Received().WithAnalyzerReferences(embeddedAnalyzers); + v2Solution.Received().AddAnalyzerReferences(embeddedAnalyzers); roslynWorkspaceWrapper.Received().TryApplyChanges(v3Solution); } @@ -192,7 +192,7 @@ public async Task OnSolutionStateChangedAsync_SolutionClosedAndReopenedAsBound_R roslynWorkspaceWrapper.CurrentSolution.Returns(v2Solution); // simulate solution closed and opened, so this is a different version now await testSubject.OnSolutionStateChangedAsync(solutionName); - v2Solution.Received().WithAnalyzerReferences(connectedAnalyzers); + v2Solution.Received().AddAnalyzerReferences(connectedAnalyzers); roslynWorkspaceWrapper.Received().TryApplyChanges(v3Solution); } @@ -213,7 +213,7 @@ public async Task OnSolutionStateChangedAsync_DifferentSolutionOpened_RegistersA Received.InOrder(() => { - v1Solution.WithAnalyzerReferences(connectedAnalyzers); + v1Solution.AddAnalyzerReferences(connectedAnalyzers); roslynWorkspaceWrapper.TryApplyChanges(v2Solution); }); } @@ -228,7 +228,7 @@ public async Task HandleConnectedModeAnalyzerUpdateAsync_Standalone_Ignores() await testSubject.HandleConnectedModeAnalyzerUpdateAsync(new AnalyzerUpdatedForConnectionEventArgs(connectedAnalyzers)); - v1Solution.DidNotReceiveWithAnyArgs().WithAnalyzerReferences(default); + v1Solution.DidNotReceiveWithAnyArgs().AddAnalyzerReferences(default); v1Solution.DidNotReceiveWithAnyArgs().RemoveAnalyzerReferences(default); } @@ -253,7 +253,7 @@ public async Task HandleConnectedModeAnalyzerUpdateAsync_Connected_NewAnalyzerSe analyzerComparer.Equals(connectedAnalyzers, differentConnectedAnalyzers); v1Solution.RemoveAnalyzerReferences(connectedAnalyzers); roslynWorkspaceWrapper.TryApplyChanges(v2Solution); - v2Solution.WithAnalyzerReferences(differentConnectedAnalyzers); + v2Solution.AddAnalyzerReferences(differentConnectedAnalyzers); roslynWorkspaceWrapper.TryApplyChanges(v3Solution); }); } @@ -315,7 +315,7 @@ private void SetUpAnalyzerAddition(IRoslynSolutionWrapper originalSolution, IRoslynSolutionWrapper resultingSolution, ImmutableArray analyzers) { - originalSolution.WithAnalyzerReferences(analyzers).Returns(resultingSolution); + originalSolution.AddAnalyzerReferences(analyzers).Returns(resultingSolution); roslynWorkspaceWrapper.TryApplyChanges(resultingSolution).Returns(true); } diff --git a/src/Infrastructure.VS/Roslyn/AnalyzerAssemblyLoaderFactory.cs b/src/Infrastructure.VS/Roslyn/AnalyzerAssemblyLoaderFactory.cs index 5778601fa7..66bfb71e34 100644 --- a/src/Infrastructure.VS/Roslyn/AnalyzerAssemblyLoaderFactory.cs +++ b/src/Infrastructure.VS/Roslyn/AnalyzerAssemblyLoaderFactory.cs @@ -51,7 +51,7 @@ public void AddDependencyLocation(string fullPath) public Assembly LoadFromPath(string fullPath) { - return Assembly.Load(fullPath); + return Assembly.LoadFrom(fullPath); } } } diff --git a/src/Infrastructure.VS/Roslyn/IRoslynSolutionWrapper.cs b/src/Infrastructure.VS/Roslyn/IRoslynSolutionWrapper.cs index 228629037d..b492387386 100644 --- a/src/Infrastructure.VS/Roslyn/IRoslynSolutionWrapper.cs +++ b/src/Infrastructure.VS/Roslyn/IRoslynSolutionWrapper.cs @@ -27,7 +27,7 @@ namespace SonarLint.VisualStudio.Infrastructure.VS.Roslyn; internal interface IRoslynSolutionWrapper { IRoslynSolutionWrapper RemoveAnalyzerReferences(ImmutableArray analyzers); - IRoslynSolutionWrapper WithAnalyzerReferences(ImmutableArray analyzers); + IRoslynSolutionWrapper AddAnalyzerReferences(ImmutableArray analyzers); Solution GetRoslynSolution(); } @@ -43,8 +43,8 @@ public IRoslynSolutionWrapper RemoveAnalyzerReferences(ImmutableArray analyzers) => - new RoslynSolutionWrapper(solution.WithAnalyzerReferences(analyzers)); + public IRoslynSolutionWrapper AddAnalyzerReferences(ImmutableArray analyzers) => + new RoslynSolutionWrapper(solution.AddAnalyzerReferences(analyzers)); public Solution GetRoslynSolution() => solution; } diff --git a/src/Infrastructure.VS/Roslyn/SolutionRoslynAnalyzerManager.cs b/src/Infrastructure.VS/Roslyn/SolutionRoslynAnalyzerManager.cs index 0dcf357d6d..e709401d52 100644 --- a/src/Infrastructure.VS/Roslyn/SolutionRoslynAnalyzerManager.cs +++ b/src/Infrastructure.VS/Roslyn/SolutionRoslynAnalyzerManager.cs @@ -186,7 +186,7 @@ private void UpdateAnalyzers(ImmutableArray analyzersToUs private void AddAnalyzer(ImmutableArray analyzerToUse) { - if (!roslynWorkspace.TryApplyChanges(roslynWorkspace.CurrentSolution.WithAnalyzerReferences(analyzerToUse))) + if (!roslynWorkspace.TryApplyChanges(roslynWorkspace.CurrentSolution.AddAnalyzerReferences(analyzerToUse))) { const string message = "Failed to add analyzer references while adding analyzers"; Debug.Assert(true, message); diff --git a/src/Integration.Vsix/Integration.Vsix.csproj b/src/Integration.Vsix/Integration.Vsix.csproj index 20c1750f64..5f38925434 100644 --- a/src/Integration.Vsix/Integration.Vsix.csproj +++ b/src/Integration.Vsix/Integration.Vsix.csproj @@ -425,6 +425,7 @@ + diff --git a/src/SLCore.UnitTests/Service/Analysis/ShouldUseEnterpriseCSharpAnalyzerParamsTests.cs b/src/SLCore.UnitTests/Service/Analysis/ShouldUseEnterpriseCSharpAnalyzerParamsTests.cs new file mode 100644 index 0000000000..0c3a0cd8b1 --- /dev/null +++ b/src/SLCore.UnitTests/Service/Analysis/ShouldUseEnterpriseCSharpAnalyzerParamsTests.cs @@ -0,0 +1,43 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2024 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 Newtonsoft.Json; +using SonarLint.VisualStudio.SLCore.Service.Analysis.Models; + +namespace SonarLint.VisualStudio.SLCore.UnitTests.Service.Analysis; + +[TestClass] +public class ShouldUseEnterpriseCSharpAnalyzerParamsTests +{ + [TestMethod] + public void Serialize_AsExpected() + { + var testSubject = new ShouldUseEnterpriseCSharpAnalyzerParams("CONFIGURATION_ID"); + const string expectedString = """ + { + "configurationScopeId": "CONFIGURATION_ID" + } + """; + + var serializedString = JsonConvert.SerializeObject(testSubject, Formatting.Indented); + + serializedString.Should().Be(expectedString); + } +} diff --git a/src/SLCore.UnitTests/Service/Analysis/ShouldUseEnterpriseCSharpAnalyzerResponseTests.cs b/src/SLCore.UnitTests/Service/Analysis/ShouldUseEnterpriseCSharpAnalyzerResponseTests.cs new file mode 100644 index 0000000000..700bd68969 --- /dev/null +++ b/src/SLCore.UnitTests/Service/Analysis/ShouldUseEnterpriseCSharpAnalyzerResponseTests.cs @@ -0,0 +1,56 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2024 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 Newtonsoft.Json; +using SonarLint.VisualStudio.SLCore.Service.Analysis.Models; +using SonarLint.VisualStudio.SLCore.Service.Telemetry; + +namespace SonarLint.VisualStudio.SLCore.UnitTests.Service.Analysis; + +[TestClass] +public class ShouldUseEnterpriseCSharpAnalyzerResponseTests +{ + [TestMethod] + public void Serialize_AsExpected() + { + var testSubject = new ShouldUseEnterpriseCSharpAnalyzerResponse(true); + const string expectedString = """ + { + "shouldUseEnterpriseAnalyzer": true + } + """; + + var serializedString = JsonConvert.SerializeObject(testSubject, Formatting.Indented); + + serializedString.Should().Be(expectedString); + } + + [TestMethod] + [DataRow(true)] + [DataRow(false)] + public void Deserialized_AsExpected(bool shouldUseEnterpriseAnalyzer) + { + var expected = new ShouldUseEnterpriseCSharpAnalyzerResponse(shouldUseEnterpriseAnalyzer); + var serialized = $"{{\"shouldUseEnterpriseAnalyzer\":{shouldUseEnterpriseAnalyzer.ToString().ToLower()}}}"; + + JsonConvert.DeserializeObject(serialized).Should().BeEquivalentTo(expected); + } +} diff --git a/src/SLCore/Service/Analysis/IRoslynAnalyzerService.cs b/src/SLCore/Service/Analysis/IRoslynAnalyzerService.cs new file mode 100644 index 0000000000..3e81754b49 --- /dev/null +++ b/src/SLCore/Service/Analysis/IRoslynAnalyzerService.cs @@ -0,0 +1,31 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2024 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.SLCore.Core; +using SonarLint.VisualStudio.SLCore.Protocol; +using SonarLint.VisualStudio.SLCore.Service.Analysis.Models; + +namespace SonarLint.VisualStudio.SLCore.Service.Analysis; + +[JsonRpcClass("analysis")] +public interface IRoslynAnalyzerService : ISLCoreService +{ + Task ShouldUseEnterpriseCSharpAnalyzerAsync(ShouldUseEnterpriseCSharpAnalyzerParams shouldUseEnterpriseCsharpParams); +} diff --git a/src/SLCore/Service/Analysis/Models/ShouldUseEnterpriseCSharpAnalyzerParams.cs b/src/SLCore/Service/Analysis/Models/ShouldUseEnterpriseCSharpAnalyzerParams.cs new file mode 100644 index 0000000000..46269b5a0d --- /dev/null +++ b/src/SLCore/Service/Analysis/Models/ShouldUseEnterpriseCSharpAnalyzerParams.cs @@ -0,0 +1,23 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2024 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.SLCore.Service.Analysis.Models; + +public record ShouldUseEnterpriseCSharpAnalyzerParams(string configurationScopeId); diff --git a/src/SLCore/Service/Analysis/Models/ShouldUseEnterpriseCSharpAnalyzerResponse.cs b/src/SLCore/Service/Analysis/Models/ShouldUseEnterpriseCSharpAnalyzerResponse.cs new file mode 100644 index 0000000000..f53be717bc --- /dev/null +++ b/src/SLCore/Service/Analysis/Models/ShouldUseEnterpriseCSharpAnalyzerResponse.cs @@ -0,0 +1,23 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2024 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.SLCore.Service.Analysis.Models; + +public record ShouldUseEnterpriseCSharpAnalyzerResponse(bool shouldUseEnterpriseAnalyzer);