From 6190782f50779a27c6a8f318b28065aadbe8d1a5 Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Fri, 31 Oct 2025 16:02:36 +0100 Subject: [PATCH] Remove analyzer redirecting DLL --- documentation/general/analyzer-redirecting.md | 12 +- sdk.slnx | 2 - ....Common.Net.Core.SDK.RuntimeAnalyzers.proj | 23 +-- .../AnalyzerRedirectingPackage.cs | 6 - ...crosoft.Net.Sdk.AnalyzerRedirecting.csproj | 44 ----- .../SdkAnalyzerAssemblyRedirector.cs | 184 ------------------ .../source.extension.vsixmanifest | 23 --- .../sdk-tasks/GenerateRuntimeAnalyzersSWR.cs | 10 - ...t.Net.Sdk.AnalyzerRedirecting.Tests.csproj | 12 -- .../SdkAnalyzerAssemblyRedirectorTests.cs | 80 -------- test/UnitTests.proj | 6 +- 11 files changed, 8 insertions(+), 394 deletions(-) delete mode 100644 src/Microsoft.Net.Sdk.AnalyzerRedirecting/AnalyzerRedirectingPackage.cs delete mode 100644 src/Microsoft.Net.Sdk.AnalyzerRedirecting/Microsoft.Net.Sdk.AnalyzerRedirecting.csproj delete mode 100644 src/Microsoft.Net.Sdk.AnalyzerRedirecting/SdkAnalyzerAssemblyRedirector.cs delete mode 100644 src/Microsoft.Net.Sdk.AnalyzerRedirecting/source.extension.vsixmanifest delete mode 100644 test/Microsoft.Net.Sdk.AnalyzerRedirecting.Tests/Microsoft.Net.Sdk.AnalyzerRedirecting.Tests.csproj delete mode 100644 test/Microsoft.Net.Sdk.AnalyzerRedirecting.Tests/SdkAnalyzerAssemblyRedirectorTests.cs diff --git a/documentation/general/analyzer-redirecting.md b/documentation/general/analyzer-redirecting.md index e999b98d76fe..993445af4ce6 100644 --- a/documentation/general/analyzer-redirecting.md +++ b/documentation/general/analyzer-redirecting.md @@ -19,11 +19,12 @@ Targeting an SDK (and hence also loading analyzers) with newer major version in ## Overview -- The SDK will contain a VSIX with the analyzer DLLs and an MEF-exported implementation of `IAnalyzerAssemblyRedirector`. +- The SDK will deploy analyzer DLLs and Roslyn will deploy an implementation of `IAnalyzerAssemblyRedirector` + (could be deployed by SDK but we are saving on analyzer loads and Roslyn already has a VSIX with a DLL). Implementations of this interface are imported by Roslyn and can redirect analyzer DLL loading. -- The SDK's implementation of `IAnalyzerAssemblyRedirector` will redirect any analyzer DLL matching some pattern - to the corresponding DLL deployed via the VSIX. +- That implementation of `IAnalyzerAssemblyRedirector` will redirect any analyzer DLL matching some pattern + to the corresponding DLL deployed with VS. Details of this process are described below. - Note that when `IAnalyzerAssemblyRedirector` is involved, Roslyn is free to not use shadow copy loading and instead load the DLLs directly. @@ -33,7 +34,7 @@ Targeting an SDK (and hence also loading analyzers) with newer major version in ## Details -The VSIX contains some analyzers, for example: +The VS deployment contains some analyzers, for example: ``` AspNetCoreAnalyzers\analyzers\dotnet\cs\Microsoft.AspNetCore.App.Analyzers.dll @@ -69,7 +70,7 @@ C:\Program Files\dotnet\sdk\9.0.100-preview.5.24307.3\Sdks\Microsoft.NET.Sdk\ana will be redirected to ``` -{VSIX}\SDKAnalyzers\Sdks\Microsoft.NET.Sdk\analyzers\Microsoft.CodeAnalysis.NetAnalyzers.dll +{InstallDir}\SDKAnalyzers\Sdks\Microsoft.NET.Sdk\analyzers\Microsoft.CodeAnalysis.NetAnalyzers.dll ``` where `metadata.json` has `"SDKAnalyzers": "9.0.100-dev"`, because @@ -83,6 +84,5 @@ Analyzers that cannot be matched will continue to be loaded from the SDK ### Implementation Analyzer DLLs are contained in transport package `VS.Redist.Common.Net.Core.SDK.RuntimeAnalyzers`. -The redirecting logic lives in "system" VS extension `Microsoft.Net.Sdk.AnalyzerRedirecting`. [torn-sdk]: https://github.com/dotnet/sdk/issues/42087 diff --git a/sdk.slnx b/sdk.slnx index 3db98db05673..603a4fd82669 100644 --- a/sdk.slnx +++ b/sdk.slnx @@ -24,7 +24,6 @@ - @@ -337,7 +336,6 @@ - diff --git a/src/Layout/VS.Redist.Common.Net.Core.SDK.RuntimeAnalyzers/VS.Redist.Common.Net.Core.SDK.RuntimeAnalyzers.proj b/src/Layout/VS.Redist.Common.Net.Core.SDK.RuntimeAnalyzers/VS.Redist.Common.Net.Core.SDK.RuntimeAnalyzers.proj index d0a01f20c9cb..2ef5859a9c0d 100644 --- a/src/Layout/VS.Redist.Common.Net.Core.SDK.RuntimeAnalyzers/VS.Redist.Common.Net.Core.SDK.RuntimeAnalyzers.proj +++ b/src/Layout/VS.Redist.Common.Net.Core.SDK.RuntimeAnalyzers/VS.Redist.Common.Net.Core.SDK.RuntimeAnalyzers.proj @@ -11,7 +11,6 @@ - @@ -30,20 +29,15 @@ - - %(RecursiveDir) - - %(RecursiveDir) - @@ -51,28 +45,13 @@ - + - - - <_VsixNamespace> - - - - - - diff --git a/src/Microsoft.Net.Sdk.AnalyzerRedirecting/AnalyzerRedirectingPackage.cs b/src/Microsoft.Net.Sdk.AnalyzerRedirecting/AnalyzerRedirectingPackage.cs deleted file mode 100644 index 8a4a84e9fc81..000000000000 --- a/src/Microsoft.Net.Sdk.AnalyzerRedirecting/AnalyzerRedirectingPackage.cs +++ /dev/null @@ -1,6 +0,0 @@ -using Microsoft.VisualStudio.Shell; - -namespace Microsoft.Net.Sdk.AnalyzerRedirecting; - -[Guid("ef89a321-14da-4de4-8f71-9bf1feea15aa")] -public sealed class AnalyzerRedirectingPackage : AsyncPackage; diff --git a/src/Microsoft.Net.Sdk.AnalyzerRedirecting/Microsoft.Net.Sdk.AnalyzerRedirecting.csproj b/src/Microsoft.Net.Sdk.AnalyzerRedirecting/Microsoft.Net.Sdk.AnalyzerRedirecting.csproj deleted file mode 100644 index 20d248f368aa..000000000000 --- a/src/Microsoft.Net.Sdk.AnalyzerRedirecting/Microsoft.Net.Sdk.AnalyzerRedirecting.csproj +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - net472 - false - true - - - - RoslynDev - true - true - true - true - false - 42.42.42.4242424 - false - - - - - - - - - - - - - - - - $(VersionPrefix).$(VersionSuffixDateStamp)$(VersionSuffixBuildOfTheDayPadded) - - - - - - - - diff --git a/src/Microsoft.Net.Sdk.AnalyzerRedirecting/SdkAnalyzerAssemblyRedirector.cs b/src/Microsoft.Net.Sdk.AnalyzerRedirecting/SdkAnalyzerAssemblyRedirector.cs deleted file mode 100644 index 23736deeb43e..000000000000 --- a/src/Microsoft.Net.Sdk.AnalyzerRedirecting/SdkAnalyzerAssemblyRedirector.cs +++ /dev/null @@ -1,184 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Immutable; -using System.ComponentModel.Composition; -using System.Text.Json; -using Microsoft.CodeAnalysis.Workspaces.AnalyzerRedirecting; -using Microsoft.VisualStudio.Shell; -using Microsoft.VisualStudio.Shell.Interop; - -// Example: -// FullPath: "C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\8.0.8\analyzers\dotnet\System.Windows.Forms.Analyzers.dll" -// ProductVersion: "8.0.8" -// PathSuffix: "analyzers\dotnet" -using AnalyzerInfo = (string FullPath, string ProductVersion, string PathSuffix); - -namespace Microsoft.Net.Sdk.AnalyzerRedirecting; - -/// -/// See documentation/general/analyzer-redirecting.md. -/// -[Export(typeof(IAnalyzerAssemblyRedirector))] -public sealed class SdkAnalyzerAssemblyRedirector : IAnalyzerAssemblyRedirector -{ - private readonly IVsActivityLog? _log; - - private readonly bool _enabled; - - private readonly string? _insertedAnalyzersDirectory; - - /// - /// Map from analyzer assembly name (file name without extension) to a list of matching analyzers. - /// - private readonly ImmutableDictionary> _analyzerMap; - - [ImportingConstructor] - public SdkAnalyzerAssemblyRedirector(SVsServiceProvider serviceProvider) : this( - Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"CommonExtensions\Microsoft\DotNet")), - serviceProvider.GetService()) - { - } - - // Internal for testing. - internal SdkAnalyzerAssemblyRedirector(string? insertedAnalyzersDirectory, IVsActivityLog? log = null) - { - _log = log; - var enable = Environment.GetEnvironmentVariable("DOTNET_ANALYZER_REDIRECTING"); - _enabled = !"0".Equals(enable, StringComparison.OrdinalIgnoreCase) && !"false".Equals(enable, StringComparison.OrdinalIgnoreCase); - _insertedAnalyzersDirectory = insertedAnalyzersDirectory; - _analyzerMap = CreateAnalyzerMap(); - } - - private ImmutableDictionary> CreateAnalyzerMap() - { - if (!_enabled) - { - Log("Analyzer redirecting is disabled."); - return ImmutableDictionary>.Empty; - } - - var metadataFilePath = Path.Combine(_insertedAnalyzersDirectory, "metadata.json"); - if (!File.Exists(metadataFilePath)) - { - Log($"File does not exist: {metadataFilePath}"); - return ImmutableDictionary>.Empty; - } - - var versions = JsonSerializer.Deserialize>(File.ReadAllText(metadataFilePath)); - if (versions is null || versions.Count == 0) - { - Log($"Versions are empty: {metadataFilePath}"); - return ImmutableDictionary>.Empty; - } - - var builder = ImmutableDictionary.CreateBuilder>(StringComparer.OrdinalIgnoreCase); - - // Expects layout like: - // VsInstallDir\DotNetRuntimeAnalyzers\WindowsDesktopAnalyzers\analyzers\dotnet\System.Windows.Forms.Analyzers.dll - // ~~~~~~~~~~~~~~~~~~~~~~~ = topLevelDirectory - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ = analyzerPath - - foreach (string topLevelDirectory in Directory.EnumerateDirectories(_insertedAnalyzersDirectory)) - { - foreach (string analyzerPath in Directory.EnumerateFiles(topLevelDirectory, "*.dll", SearchOption.AllDirectories)) - { - if (!analyzerPath.StartsWith(topLevelDirectory, StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - string subsetName = Path.GetFileName(topLevelDirectory); - if (!versions.TryGetValue(subsetName, out string version)) - { - continue; - } - - string analyzerName = Path.GetFileNameWithoutExtension(analyzerPath); - string pathSuffix = analyzerPath.Substring(topLevelDirectory.Length + 1 /* slash */); - pathSuffix = Path.GetDirectoryName(pathSuffix); - - AnalyzerInfo analyzer = new() { FullPath = analyzerPath, ProductVersion = version, PathSuffix = pathSuffix }; - - if (builder.TryGetValue(analyzerName, out var existing)) - { - existing.Add(analyzer); - } - else - { - builder.Add(analyzerName, [analyzer]); - } - } - } - - Log($"Loaded analyzer map ({builder.Count}): {_insertedAnalyzersDirectory}"); - - return builder.ToImmutable(); - } - - public string? RedirectPath(string fullPath) - { - if (_enabled && _analyzerMap.TryGetValue(Path.GetFileNameWithoutExtension(fullPath), out var analyzers)) - { - foreach (AnalyzerInfo analyzer in analyzers) - { - var directoryPath = Path.GetDirectoryName(fullPath); - - // Note that both paths we compare here are normalized via netfx's Path.GetDirectoryName. - if (directoryPath.EndsWith(analyzer.PathSuffix, StringComparison.OrdinalIgnoreCase) && - majorAndMinorVersionsMatch(directoryPath, analyzer.PathSuffix, analyzer.ProductVersion)) - { - return analyzer.FullPath; - } - } - } - - return null; - - static bool majorAndMinorVersionsMatch(string directoryPath, string pathSuffix, string version) - { - // Find the version number in the directory path - it is in the directory name before the path suffix. - // Example: - // "C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\8.0.8\analyzers\dotnet\" = directoryPath - // ~~~~~~~~~~~~~~~~ = pathSuffix - // ~~~~~ = directoryPathVersion - // This can match also a NuGet package because the version number is at the same position: - // "C:\.nuget\packages\Microsoft.WindowsDesktop.App.Ref\8.0.8\analyzers\dotnet\" - - int index = directoryPath.LastIndexOf(pathSuffix, StringComparison.OrdinalIgnoreCase); - if (index < 0) - { - return false; - } - - string directoryPathVersion = Path.GetFileName(Path.GetDirectoryName(directoryPath.Substring(0, index))); - - return areVersionMajorMinorPartEqual(directoryPathVersion, version); - } - - static bool areVersionMajorMinorPartEqual(string version1, string version2) - { - int firstDotIndex = version1.IndexOf('.'); - if (firstDotIndex < 0) - { - return false; - } - - int secondDotIndex = version1.IndexOf('.', firstDotIndex + 1); - if (secondDotIndex < 0) - { - return false; - } - - return 0 == string.Compare(version1, 0, version2, 0, secondDotIndex, StringComparison.OrdinalIgnoreCase); - } - } - - private void Log(string message) - { - _log?.LogEntry( - (uint)__ACTIVITYLOG_ENTRYTYPE.ALE_INFORMATION, - nameof(SdkAnalyzerAssemblyRedirector), - message); - } -} diff --git a/src/Microsoft.Net.Sdk.AnalyzerRedirecting/source.extension.vsixmanifest b/src/Microsoft.Net.Sdk.AnalyzerRedirecting/source.extension.vsixmanifest deleted file mode 100644 index cfa767fb3ca0..000000000000 --- a/src/Microsoft.Net.Sdk.AnalyzerRedirecting/source.extension.vsixmanifest +++ /dev/null @@ -1,23 +0,0 @@ - - - - - .NET SDK Analyzer Redirecting - .NET SDK Analyzer Redirecting Package. - - - - - - - - - - - - - - amd64 - - - diff --git a/src/Tasks/sdk-tasks/GenerateRuntimeAnalyzersSWR.cs b/src/Tasks/sdk-tasks/GenerateRuntimeAnalyzersSWR.cs index fd5d0e6370ba..cd24e6ebd826 100644 --- a/src/Tasks/sdk-tasks/GenerateRuntimeAnalyzersSWR.cs +++ b/src/Tasks/sdk-tasks/GenerateRuntimeAnalyzersSWR.cs @@ -29,16 +29,6 @@ public override bool Execute() "metadata.json", ]); - AddFolder(sb, - "AnalyzerRedirecting", - @$"{installDir}\AnalyzerRedirecting", - filesToInclude: - [ - "Microsoft.Net.Sdk.AnalyzerRedirecting.dll", - "Microsoft.Net.Sdk.AnalyzerRedirecting.pkgdef", - "extension.vsixmanifest", - ]); - AddFolder(sb, "AspNetCoreAnalyzers", @$"{installDir}\AspNetCoreAnalyzers"); diff --git a/test/Microsoft.Net.Sdk.AnalyzerRedirecting.Tests/Microsoft.Net.Sdk.AnalyzerRedirecting.Tests.csproj b/test/Microsoft.Net.Sdk.AnalyzerRedirecting.Tests/Microsoft.Net.Sdk.AnalyzerRedirecting.Tests.csproj deleted file mode 100644 index 3867729d2172..000000000000 --- a/test/Microsoft.Net.Sdk.AnalyzerRedirecting.Tests/Microsoft.Net.Sdk.AnalyzerRedirecting.Tests.csproj +++ /dev/null @@ -1,12 +0,0 @@ - - - - net472 - - - - - - - - diff --git a/test/Microsoft.Net.Sdk.AnalyzerRedirecting.Tests/SdkAnalyzerAssemblyRedirectorTests.cs b/test/Microsoft.Net.Sdk.AnalyzerRedirecting.Tests/SdkAnalyzerAssemblyRedirectorTests.cs deleted file mode 100644 index 956161b134a8..000000000000 --- a/test/Microsoft.Net.Sdk.AnalyzerRedirecting.Tests/SdkAnalyzerAssemblyRedirectorTests.cs +++ /dev/null @@ -1,80 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Text.Json; - -namespace Microsoft.Net.Sdk.AnalyzerRedirecting.Tests; - -public class SdkAnalyzerAssemblyRedirectorTests(ITestOutputHelper log) : SdkTest(log) -{ - [Theory] - [InlineData("9.0.0-preview.5.24306.11", "9.0.0-preview.7.24406.2")] - [InlineData("9.0.0-preview.5.24306.11", "9.0.1-preview.7.24406.2")] - [InlineData("9.0.100", "9.0.0-preview.7.24406.2")] - [InlineData("9.0.100", "9.0.200")] - [InlineData("9.0.100", "9.0.101")] - public void SameMajorMinorVersion(string a, string b) - { - TestDirectory testDir = _testAssetsManager.CreateTestDirectory(identifier: "RuntimeAnalyzers"); - - var vsDir = Path.Combine(testDir.Path, "vs"); - Metadata(vsDir, new() { { "AspNetCoreAnalyzers", a } }); - var vsAnalyzerPath = FakeDll(vsDir, @$"AspNetCoreAnalyzers\analyzers\dotnet\cs", "Microsoft.AspNetCore.App.Analyzers"); - var sdkAnalyzerPath = FakeDll(testDir.Path, @$"sdk\packs\Microsoft.AspNetCore.App.Ref\{b}\analyzers\dotnet\cs", "Microsoft.AspNetCore.App.Analyzers"); - - var resolver = new SdkAnalyzerAssemblyRedirector(vsDir); - var redirected = resolver.RedirectPath(sdkAnalyzerPath); - redirected.Should().Be(vsAnalyzerPath); - } - - [Fact] - public void DifferentPathSuffix() - { - TestDirectory testDir = _testAssetsManager.CreateTestDirectory(identifier: "RuntimeAnalyzers"); - - var vsDir = Path.Combine(testDir.Path, "vs"); - Metadata(vsDir, new() { { "AspNetCoreAnalyzers", "9.0.0-preview.5.24306.11" } }); - FakeDll(vsDir, @"AspNetCoreAnalyzers\analyzers\dotnet\cs", "Microsoft.AspNetCore.App.Analyzers"); - var sdkAnalyzerPath = FakeDll(testDir.Path, @"sdk\packs\Microsoft.AspNetCore.App.Ref\9.0.0-preview.7.24406.2\analyzers\dotnet\vb", "Microsoft.AspNetCore.App.Analyzers"); - - var resolver = new SdkAnalyzerAssemblyRedirector(vsDir); - var redirected = resolver.RedirectPath(sdkAnalyzerPath); - redirected.Should().BeNull(); - } - - [Theory] - [InlineData("8.0.100", "9.0.0-preview.7.24406.2")] - [InlineData("9.1.100", "9.0.0-preview.7.24406.2")] - [InlineData("9.1.0-preview.5.24306.11", "9.0.0-preview.7.24406.2")] - [InlineData("9.0.100", "9.1.100")] - [InlineData("9.0.100", "10.0.100")] - [InlineData("9.9.100", "9.10.100")] - public void DifferentMajorMinorVersion(string a, string b) - { - TestDirectory testDir = _testAssetsManager.CreateTestDirectory(identifier: "RuntimeAnalyzers"); - - var vsDir = Path.Combine(testDir.Path, "vs"); - Metadata(vsDir, new() { { "AspNetCoreAnalyzers", a } }); - FakeDll(vsDir, @$"AspNetCoreAnalyzers\analyzers\dotnet\cs", "Microsoft.AspNetCore.App.Analyzers"); - var sdkAnalyzerPath = FakeDll(testDir.Path, @$"sdk\packs\Microsoft.AspNetCore.App.Ref\{b}\analyzers\dotnet\cs", "Microsoft.AspNetCore.App.Analyzers"); - - var resolver = new SdkAnalyzerAssemblyRedirector(vsDir); - var redirected = resolver.RedirectPath(sdkAnalyzerPath); - redirected.Should().BeNull(); - } - - private static string FakeDll(string root, string subdir, string name) - { - var dllPath = Path.Combine(root, subdir, $"{name}.dll"); - Directory.CreateDirectory(Path.GetDirectoryName(dllPath)); - File.WriteAllText(dllPath, ""); - return dllPath; - } - - private static void Metadata(string root, Dictionary versions) - { - var metadataFilePath = Path.Combine(root, "metadata.json"); - Directory.CreateDirectory(Path.GetDirectoryName(metadataFilePath)); - File.WriteAllText(metadataFilePath, JsonSerializer.Serialize(versions)); - } -} diff --git a/test/UnitTests.proj b/test/UnitTests.proj index 6c396845af1d..f043119e1b05 100644 --- a/test/UnitTests.proj +++ b/test/UnitTests.proj @@ -16,12 +16,8 @@ - + - - net472 - net472 -