Skip to content

Commit

Permalink
Part of SLVS-1497
Browse files Browse the repository at this point in the history
Do not expose AnalyzerFileReference, which has a reference to Microsoft.CodeAnalysis
  • Loading branch information
gabriela-trutan-sonarsource committed Oct 31, 2024
1 parent 3592f69 commit 8a36ca1
Show file tree
Hide file tree
Showing 8 changed files with 83 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,13 @@ public void Get_AnalyzerFilesExist_ReturnsAnalyzerFileReference()
{
locator.GetBasicAnalyzerFullPaths().Returns([GetAnalyzerFullPath("analyzer1.dll"), GetAnalyzerFullPath("analyzer2.dll")]);

var analyzerFileReferences = testSubject.Get();
var analyzerReferencesHolder = testSubject.Get();

analyzerAssemblyLoaderFactory.Received(1).Create();
analyzerFileReferences.Should().NotBeNull();
analyzerFileReferences.Length.Should().Be(2);
ContainsExpectedAnalyzerFileReference(analyzerFileReferences, GetAnalyzerFullPath("analyzer1.dll"));
ContainsExpectedAnalyzerFileReference(analyzerFileReferences, GetAnalyzerFullPath("analyzer2.dll"));
analyzerReferencesHolder.Should().NotBeNull();
analyzerReferencesHolder.AnalyzerFileReferences.Length.Should().Be(2);
ContainsExpectedAnalyzerFileReference(analyzerReferencesHolder.AnalyzerFileReferences, GetAnalyzerFullPath("analyzer1.dll"));
ContainsExpectedAnalyzerFileReference(analyzerReferencesHolder.AnalyzerFileReferences, GetAnalyzerFullPath("analyzer2.dll"));
}

[TestMethod]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ public class SolutionRoslynAnalyzerManagerTests
private IEqualityComparer<ImmutableArray<AnalyzerFileReference>?> analyzerComparer;
private ILogger logger;
private SolutionRoslynAnalyzerManager testSubject;
private readonly IAnalyzerReferencesHolder embeddedAnalyzersHolder = Substitute.For<IAnalyzerReferencesHolder>();
private readonly IAnalyzerReferencesHolder connectedAnalyzersHolder = Substitute.For<IAnalyzerReferencesHolder>();
private readonly ImmutableArray<AnalyzerFileReference> embeddedAnalyzers = ImmutableArray.Create(new AnalyzerFileReference(@"C:\path\embedded", Substitute.For<IAnalyzerAssemblyLoader>()));
private readonly ImmutableArray<AnalyzerFileReference> connectedAnalyzers = ImmutableArray.Create(new AnalyzerFileReference(@"C:\path\connected", Substitute.For<IAnalyzerAssemblyLoader>()));

Expand All @@ -59,6 +61,9 @@ public void TestInitialize()
asyncLockFactory = Substitute.For<IAsyncLockFactory>();
asyncLock = Substitute.For<IAsyncLock>();
asyncLockFactory.Create().Returns(asyncLock);
embeddedAnalyzersHolder.AnalyzerFileReferences.Returns(embeddedAnalyzers);
connectedAnalyzersHolder.AnalyzerFileReferences.Returns(connectedAnalyzers);
embeddedRoslynAnalyzerProvider.Get().Returns(embeddedAnalyzersHolder);

testSubject = new SolutionRoslynAnalyzerManager(
embeddedRoslynAnalyzerProvider,
Expand All @@ -70,7 +75,7 @@ public void TestInitialize()
asyncLockFactory,
logger);
}

[TestMethod]
public void MefCtor_CheckIsExported()
{
Expand Down Expand Up @@ -103,7 +108,7 @@ public void Ctor_SubscribesToEvents()
asyncLockFactory,
logger);

// todo add more tests for the events

activeConfigScopeTracker.Received().CurrentConfigurationScopeChanged += Arg.Any<EventHandler>();
activeSolutionTracker.Received().ActiveSolutionChanged += Arg.Any<EventHandler<ActiveSolutionChangedEventArgs>>();
}
Expand All @@ -127,14 +132,14 @@ public async Task OnSolutionStateChangedAsync_AcquiresLock()
[TestMethod]
public async Task OnSolutionStateChangedAsync_StandaloneSolution_AppliesEmbeddedAnalyzer()
{
embeddedRoslynAnalyzerProvider.Get()
.Returns(embeddedAnalyzers);
var solutionName = "solution";
var v1Solution = Substitute.For<IRoslynSolutionWrapper>();
var v2Solution = Substitute.For<IRoslynSolutionWrapper>();
SetUpCurrentSolutionSequence(v1Solution);
SetUpAnalyzerAddition(v1Solution, v2Solution, embeddedAnalyzers);
SetupGetConnectedModeAnalyzersReturnsNull();

await testSubject.OnSolutionStateChangedAsync("solution");
await testSubject.OnSolutionStateChangedAsync(solutionName);

roslynWorkspaceWrapper.Received().TryApplyChanges(v2Solution);
}
Expand All @@ -149,7 +154,7 @@ public async Task OnSolutionStateChangedAsync_StandaloneSolution_BindingSet_Remo
await SetUpStandaloneSolution(v1Solution, solutionName);

analyzerComparer.Equals(embeddedAnalyzers, connectedAnalyzers).Returns(false);
connectedModeRoslynAnalyzerProvider.GetOrNullAsync().Returns(connectedAnalyzers);
connectedModeRoslynAnalyzerProvider.GetOrNullAsync().Returns(connectedAnalyzersHolder);
roslynWorkspaceWrapper.CurrentSolution.Returns(v1Solution, v2Solution);
SetUpAnalyzerRemoval(v1Solution, v2Solution, embeddedAnalyzers);
SetUpAnalyzerAddition(v2Solution, v3Solution, connectedAnalyzers);
Expand All @@ -173,8 +178,6 @@ public async Task OnSolutionStateChangedAsync_StandaloneSolution_BindingSet_NoCo
const string solutionName = "solution";
var v1Solution = Substitute.For<IRoslynSolutionWrapper>();
await SetUpStandaloneSolution(v1Solution, solutionName);
EnableDefaultEmbeddedAnalyzers();
connectedModeRoslynAnalyzerProvider.GetOrNullAsync().Returns((ImmutableArray<AnalyzerFileReference>?)null);
analyzerComparer.Equals(embeddedAnalyzers, embeddedAnalyzers).Returns(true);

await testSubject.OnSolutionStateChangedAsync(solutionName);
Expand All @@ -196,7 +199,6 @@ public async Task OnSolutionStateChangedAsync_SolutionClosedAndReopened_Register
var v2Solution = Substitute.For<IRoslynSolutionWrapper>();
var v3Solution = Substitute.For<IRoslynSolutionWrapper>();
await SetUpStandaloneSolution(preCloseSolution, solutionName);
EnableDefaultEmbeddedAnalyzers();
SetUpCurrentSolutionSequence(v2Solution);
SetUpAnalyzerAddition(v2Solution, v3Solution, embeddedAnalyzers);
SetUpAnalyzerRemoval(v2Solution, v3Solution, embeddedAnalyzers);
Expand All @@ -216,8 +218,7 @@ public async Task OnSolutionStateChangedAsync_SolutionClosedAndReopenedAsBound_R
var v2Solution = Substitute.For<IRoslynSolutionWrapper>();
var v3Solution = Substitute.For<IRoslynSolutionWrapper>();
await SetUpStandaloneSolution(preCloseSolution, solutionName);
EnableDefaultEmbeddedAnalyzers();
connectedModeRoslynAnalyzerProvider.GetOrNullAsync().Returns(connectedAnalyzers);
connectedModeRoslynAnalyzerProvider.GetOrNullAsync().Returns(connectedAnalyzersHolder);
SetUpAnalyzerAddition(v2Solution, v3Solution, connectedAnalyzers);
SetUpAnalyzerRemoval(preCloseSolution, v2Solution, embeddedAnalyzers);

Expand All @@ -236,9 +237,8 @@ public async Task OnSolutionStateChangedAsync_DifferentSolutionOpened_RegistersA
var v1Solution = Substitute.For<IRoslynSolutionWrapper>();
var v2Solution = Substitute.For<IRoslynSolutionWrapper>();
await SetUpStandaloneSolution(originalSolution, "original solution");
EnableDefaultEmbeddedAnalyzers();

connectedModeRoslynAnalyzerProvider.GetOrNullAsync().Returns(connectedAnalyzers);
connectedModeRoslynAnalyzerProvider.GetOrNullAsync().Returns(connectedAnalyzersHolder);
SetUpCurrentSolutionSequence(v1Solution); // different solution
SetUpAnalyzerAddition(v1Solution, v2Solution, connectedAnalyzers);
SetUpAnalyzerRemoval(v1Solution, v2Solution, embeddedAnalyzers);
Expand Down Expand Up @@ -273,7 +273,7 @@ public void OnSolutionStateChangedAsync_Disposed_Throws()
}

[TestMethod]
public async Task OnSolutionStateChangedAsync_SolutionClosed_RemovesAnalzyers()
public async Task OnSolutionStateChangedAsync_SolutionClosed_RemovesAnalyzers()
{
var v1Solution = Substitute.For<IRoslynSolutionWrapper>();
await SetUpStandaloneSolution(v1Solution, "solution");
Expand All @@ -289,7 +289,7 @@ public async Task OnSolutionStateChangedAsync_SolutionClosed_RemovesAnalzyers()
public async Task CurrentConfigurationScopeChanged_SetsAnalyzers()
{
roslynWorkspaceWrapper.TryApplyChanges(Arg.Any<IRoslynSolutionWrapper>()).Returns(true);
connectedModeRoslynAnalyzerProvider.GetOrNullAsync().Returns(embeddedAnalyzers);
connectedModeRoslynAnalyzerProvider.GetOrNullAsync().Returns(embeddedAnalyzersHolder);
activeConfigScopeTracker.Current.Returns(new ConfigurationScope("my solution"));

activeConfigScopeTracker.CurrentConfigurationScopeChanged += Raise.Event<EventHandler>(this, EventArgs.Empty);
Expand All @@ -302,7 +302,7 @@ public async Task CurrentConfigurationScopeChanged_SetsAnalyzers()
public async Task ActiveSolutionChanged_SetsAnalyzers()
{
roslynWorkspaceWrapper.TryApplyChanges(Arg.Any<IRoslynSolutionWrapper>()).Returns(true);
connectedModeRoslynAnalyzerProvider.GetOrNullAsync().Returns(embeddedAnalyzers);
connectedModeRoslynAnalyzerProvider.GetOrNullAsync().Returns(embeddedAnalyzersHolder);

activeSolutionTracker.ActiveSolutionChanged += Raise.Event<EventHandler<ActiveSolutionChangedEventArgs>>(this, new ActiveSolutionChangedEventArgs(true, "my solution"));

Expand Down Expand Up @@ -331,15 +331,15 @@ private void SetUpAnalyzerRemoval(IRoslynSolutionWrapper originalSolution,
roslynWorkspaceWrapper.TryApplyChanges(resultingSolution).Returns(true);
}

private void EnableDefaultEmbeddedAnalyzers()
private async Task SetUpStandaloneSolution(IRoslynSolutionWrapper solution, string solutionName)
{
embeddedRoslynAnalyzerProvider.Get().Returns(embeddedAnalyzers);
SetupGetConnectedModeAnalyzersReturnsNull();
await SimulateSolutionSet(solution, solutionName, embeddedAnalyzers);
}

private async Task SetUpStandaloneSolution(IRoslynSolutionWrapper solution, string solutionName)
private void SetupGetConnectedModeAnalyzersReturnsNull()
{
EnableDefaultEmbeddedAnalyzers();
await SimulateSolutionSet(solution, solutionName, embeddedAnalyzers);
connectedModeRoslynAnalyzerProvider.GetOrNullAsync().Returns((IAnalyzerReferencesHolder)null);
}

private async Task SimulateSolutionSet(IRoslynSolutionWrapper resultingSolution, string solutionName, ImmutableArray<AnalyzerFileReference> analyzers)
Expand All @@ -354,9 +354,9 @@ private async Task SimulateSolutionSet(IRoslynSolutionWrapper resultingSolution,

private void ClearSubstitutes()
{
embeddedRoslynAnalyzerProvider.ClearSubstitute();
connectedModeRoslynAnalyzerProvider.ClearSubstitute();
analyzerComparer.ClearSubstitute();
roslynWorkspaceWrapper.ClearSubstitute();
embeddedRoslynAnalyzerProvider.ClearReceivedCalls();
connectedModeRoslynAnalyzerProvider.ClearReceivedCalls();
analyzerComparer.ClearReceivedCalls();
roslynWorkspaceWrapper.ClearReceivedCalls();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

using System.Collections.Immutable;
using System.ComponentModel.Composition;
using Microsoft.CodeAnalysis.Diagnostics;
using SonarLint.VisualStudio.Core.Binding;

namespace SonarLint.VisualStudio.Infrastructure.VS.Roslyn;

Expand All @@ -34,9 +31,9 @@ public ConnectedModeRoslynAnalyzerProvider()
{
}

public async Task<ImmutableArray<AnalyzerFileReference>?> GetOrNullAsync()
public async Task<IAnalyzerReferencesHolder> GetOrNullAsync()
{
return await Task.FromResult<ImmutableArray<AnalyzerFileReference>?>(null);
return await Task.FromResult<IAnalyzerReferencesHolder>(null);
}

public event EventHandler<AnalyzerUpdatedForConnectionEventArgs> AnalyzerUpdatedForConnection;
Expand Down
10 changes: 5 additions & 5 deletions src/Infrastructure.VS/Roslyn/EmbeddedRoslynAnalyzerProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public class EmbeddedRoslynAnalyzerProvider : IEmbeddedRoslynAnalyzerProvider
private readonly IEmbeddedDotnetAnalyzersLocator locator;
private readonly IAnalyzerAssemblyLoaderFactory analyzerAssemblyLoaderFactory;
private readonly ILogger logger;
private ImmutableArray<AnalyzerFileReference>? embeddedAnalyzers;
private IAnalyzerReferencesHolder embeddedAnalyzers;

[ImportingConstructor]
public EmbeddedRoslynAnalyzerProvider(IEmbeddedDotnetAnalyzersLocator locator, ILogger logger) :
Expand All @@ -49,14 +49,14 @@ internal EmbeddedRoslynAnalyzerProvider(IEmbeddedDotnetAnalyzersLocator locator,
this.logger = logger;
}

public ImmutableArray<AnalyzerFileReference> Get()
public IAnalyzerReferencesHolder Get()
{
embeddedAnalyzers ??= CreateAnalyzerFileReferences();

return embeddedAnalyzers.Value;
return embeddedAnalyzers;
}

private ImmutableArray<AnalyzerFileReference> CreateAnalyzerFileReferences()
private IAnalyzerReferencesHolder CreateAnalyzerFileReferences()
{
var analyzerPaths = locator.GetBasicAnalyzerFullPaths();
if(analyzerPaths.Count == 0)
Expand All @@ -65,6 +65,6 @@ private ImmutableArray<AnalyzerFileReference> CreateAnalyzerFileReferences()
throw new InvalidOperationException(Resources.EmbeddedRoslynAnalyzersNotFound);
}
var loader = analyzerAssemblyLoaderFactory.Create();
return analyzerPaths.Select(path => new AnalyzerFileReference(path, loader)).ToImmutableArray();
return new AnalyzerReferencesHolder(analyzerPaths, loader);
}
}
40 changes: 40 additions & 0 deletions src/Infrastructure.VS/Roslyn/IAnalyzerReferencesHolder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* 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 System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;

namespace SonarLint.VisualStudio.Infrastructure.VS.Roslyn;

public interface IAnalyzerReferencesHolder
{
ImmutableArray<AnalyzerFileReference> AnalyzerFileReferences { get; }
}

public class AnalyzerReferencesHolder : IAnalyzerReferencesHolder
{
public AnalyzerReferencesHolder(List<string> analyzerPaths, IAnalyzerAssemblyLoader analyzerAssemblyLoader)
{
AnalyzerFileReferences = analyzerPaths.Select(path => new AnalyzerFileReference(path, analyzerAssemblyLoader)).ToImmutableArray();
}

public ImmutableArray<AnalyzerFileReference> AnalyzerFileReferences { get; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using SonarLint.VisualStudio.Core.Binding;

namespace SonarLint.VisualStudio.Infrastructure.VS.Roslyn;

Expand All @@ -30,7 +29,7 @@ public interface IConnectedModeRoslynAnalyzerProvider
/// <summary>
/// Returns SonarAnalyzer.CSharp & SonarAnalyzer.VisualBasic analyzer DLLs that are downloaded from the server for the current binding
/// </summary>
Task<ImmutableArray<AnalyzerFileReference>?> GetOrNullAsync();
Task<IAnalyzerReferencesHolder> GetOrNullAsync();

/// <summary>
/// Provides updates about the analyzers for a given connection
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,6 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

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

namespace SonarLint.VisualStudio.Infrastructure.VS.Roslyn;

public interface IEmbeddedRoslynAnalyzerProvider
Expand All @@ -29,5 +26,5 @@ public interface IEmbeddedRoslynAnalyzerProvider
/// Returns SonarAnalyzer.CSharp & SonarAnalyzer.VisualBasic analyzer DLLs that are embedded in the VSIX.
/// If no analyzer is found, throws an exception
/// </summary>
ImmutableArray<AnalyzerFileReference> Get();
IAnalyzerReferencesHolder Get();
}
8 changes: 4 additions & 4 deletions src/Infrastructure.VS/Roslyn/SolutionRoslynAnalyzerManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@ public SolutionRoslynAnalyzerManager(
return;
}

var analyzersToUse = await ChooseAnalyzersAsync();
UpdateAnalyzersIfChanged(analyzersToUse);
var analyzersReferencesHolder = await ChooseAnalyzersAsync();
UpdateAnalyzersIfChanged(analyzersReferencesHolder.AnalyzerFileReferences);
}
}

Expand All @@ -126,10 +126,10 @@ public void Dispose()
disposed = true;
}

private async Task<ImmutableArray<AnalyzerFileReference>> ChooseAnalyzersAsync() =>
private async Task<IAnalyzerReferencesHolder> ChooseAnalyzersAsync() =>
ChooseAnalyzers(await connectedModeAnalyzerProvider.GetOrNullAsync());

private ImmutableArray<AnalyzerFileReference> ChooseAnalyzers(ImmutableArray<AnalyzerFileReference>? connectedModeAnalyzers) =>
private IAnalyzerReferencesHolder ChooseAnalyzers(IAnalyzerReferencesHolder connectedModeAnalyzers) =>
connectedModeAnalyzers ?? embeddedAnalyzerProvider.Get();

private void UpdateAnalyzersIfChanged(ImmutableArray<AnalyzerFileReference> analyzersToUse)
Expand Down

0 comments on commit 8a36ca1

Please sign in to comment.