Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
62154cf
Add analyzer redirecting VSIX
jjonescz Aug 20, 2024
cdb4892
Fix infra
jjonescz Aug 26, 2024
2ed93ed
Merge branch 'main' into analyzer-redirecting
jjonescz Aug 26, 2024
53a5074
Add to UnitTests.proj
jjonescz Aug 26, 2024
5928f32
Implement path redirect
jjonescz Aug 29, 2024
5165f0f
Merge branch 'main' into analyzer-redirecting
jjonescz Aug 29, 2024
9807811
Implement redirector API
jjonescz Aug 30, 2024
4e01a87
Improve code
jjonescz Aug 30, 2024
cdac6cc
Update roslyn
jjonescz Sep 2, 2024
7a31ffb
Simplify slash handling
jjonescz Sep 2, 2024
586ced5
Simplify version matching
jjonescz Sep 2, 2024
c4a206a
Merge branch 'main' into analyzer-redirecting
jjonescz Sep 2, 2024
e65c1c0
Add transport package for VS deployment
jjonescz Sep 4, 2024
61af4e8
Merge branch 'main' into analyzer-redirecting
jjonescz Sep 4, 2024
245b2f1
Consume the API from Workspaces
jjonescz Sep 9, 2024
f5d671e
Update roslyn
jjonescz Sep 10, 2024
92e2284
Merge branch 'main' into analyzer-redirecting
jjonescz Sep 10, 2024
f0b86a4
Add comments
jjonescz Sep 13, 2024
d5a3924
Merge branch 'main' into analyzer-redirecting
jjonescz Jan 13, 2025
5da7cda
Remove Lazy
jjonescz Jan 15, 2025
6a8f110
Extend tests
jjonescz Jan 15, 2025
8680087
Update Roslyn
jjonescz Jan 15, 2025
f73918a
Merge branch 'main' into analyzer-redirecting
jjonescz Jan 15, 2025
74518c1
Extend tests
jjonescz Jan 16, 2025
d8195ab
Merge branch 'main' into analyzer-redirecting
jjonescz Feb 14, 2025
cf5c1f3
Remove testing feed
jjonescz Feb 14, 2025
8b61147
Disable GenerateRuntimeAnalyzersSWR in VMR build
jjonescz Feb 14, 2025
7116b32
Merge branch 'main' into analyzer-redirecting
jjonescz Feb 20, 2025
4c32363
Do not NGEN the new assemblies for now
jjonescz Feb 26, 2025
bde0f8d
Merge branch 'main' into analyzer-redirecting
jjonescz Feb 28, 2025
1971566
Explain why VsixVersion is needed
jjonescz Mar 10, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
<PackageVersion Include="Microsoft.TestPlatform.Build" Version="$(MicrosoftTestPlatformBuildPackageVersion)" />
<PackageVersion Include="Microsoft.TestPlatform.CLI" Version="$(MicrosoftTestPlatformCLIPackageVersion)" />
<PackageVersion Include="Microsoft.VisualStudio.Composition" Version="17.4.16" />
<PackageVersion Include="Microsoft.VisualStudio.Sdk" Version="17.2.32505.173" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you know how often these get updated and how best to keep them updated?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This gets a non-preview release with every VS minor version. But I don't think this needs to be updated unless a new functionality from the VS SDK is needed (or if there are some transitive dependency conflicts). That's why it's fine that this is using an old version 17.2 instead of the latest 17.13 (but the real reason I chose 17.2 was because there were some conflicts with transitive dependencies IIRC).

<PackageVersion Include="Microsoft.VisualStudio.Setup.Configuration.Interop" Version="$(MicrosoftVisualStudioSetupConfigurationInteropVersion)" />
<PackageVersion Include="Microsoft.Web.Deployment" Version="$(WebDeploymentPackageVersion)" />
<PackageVersion Include="Microsoft.Web.Xdt" Version="$(MicrosoftWebXdtPackageVersion)" />
Expand Down
3 changes: 3 additions & 0 deletions NuGet.config
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,12 @@
<add key="dotnet-tools-transport" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools-transport/nuget/v3/index.json" />
<add key="dotnet-libraries" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-libraries/nuget/v3/index.json" />
<add key="dotnet-libraries-transport" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-libraries-transport/nuget/v3/index.json" />
<add key="vssdk" value="https://pkgs.dev.azure.com/azure-public/vside/_packaging/vssdk/nuget/v3/index.json" />
<add key="vssdk-archived" value="https://pkgs.dev.azure.com/azure-public/vside/_packaging/vssdk-archived/nuget/v3/index.json" />
<add key="vs-impl" value="https://pkgs.dev.azure.com/azure-public/vside/_packaging/vs-impl/nuget/v3/index.json" />
<!-- Used for Rich Navigation indexing task -->
<add key="richnav" value="https://pkgs.dev.azure.com/azure-public/vside/_packaging/vs-buildservices/nuget/v3/index.json" />
<add key="general-testing" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/general-testing/nuget/v3/index.json" />
</packageSources>
<disabledPackageSources>
<!--Begin: Package sources managed by Dependency Flow automation. Do not edit the sources below.-->
Expand Down
36 changes: 18 additions & 18 deletions eng/Version.Details.xml
Original file line number Diff line number Diff line change
Expand Up @@ -93,43 +93,43 @@
<Sha>a5feb419073e74562fde38768898988334f379a1</Sha>
<SourceBuild RepoName="fsharp" ManagedOnly="true" />
</Dependency>
<Dependency Name="Microsoft.Net.Compilers.Toolset" Version="4.12.0-3.24427.16">
<Dependency Name="Microsoft.Net.Compilers.Toolset" Version="4.12.0-3.24428.2">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>a3dbd808a5ee9b8010d38f37726eeb596e2f3aaa</Sha>
<Sha>8e5e9ad66e5a8137253bfb57448e0fef4d7a6e0f</Sha>
</Dependency>
<!-- Intermediate is necessary for source build. -->
<Dependency Name="Microsoft.SourceBuild.Intermediate.roslyn" Version="4.12.0-3.24427.16">
<Dependency Name="Microsoft.SourceBuild.Intermediate.roslyn" Version="4.12.0-3.24428.2">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>a3dbd808a5ee9b8010d38f37726eeb596e2f3aaa</Sha>
<Sha>8e5e9ad66e5a8137253bfb57448e0fef4d7a6e0f</Sha>
<SourceBuild RepoName="roslyn" ManagedOnly="true" />
</Dependency>
<Dependency Name="Microsoft.Net.Compilers.Toolset.Framework" Version="4.12.0-3.24427.16">
<Dependency Name="Microsoft.Net.Compilers.Toolset.Framework" Version="4.12.0-3.24428.2">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>a3dbd808a5ee9b8010d38f37726eeb596e2f3aaa</Sha>
<Sha>8e5e9ad66e5a8137253bfb57448e0fef4d7a6e0f</Sha>
</Dependency>
<Dependency Name="Microsoft.CodeAnalysis" Version="4.12.0-3.24427.16">
<Dependency Name="Microsoft.CodeAnalysis" Version="4.12.0-3.24428.2">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>a3dbd808a5ee9b8010d38f37726eeb596e2f3aaa</Sha>
<Sha>8e5e9ad66e5a8137253bfb57448e0fef4d7a6e0f</Sha>
</Dependency>
<Dependency Name="Microsoft.CodeAnalysis.CSharp" Version="4.12.0-3.24427.16">
<Dependency Name="Microsoft.CodeAnalysis.CSharp" Version="4.12.0-3.24428.2">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>a3dbd808a5ee9b8010d38f37726eeb596e2f3aaa</Sha>
<Sha>8e5e9ad66e5a8137253bfb57448e0fef4d7a6e0f</Sha>
</Dependency>
<Dependency Name="Microsoft.CodeAnalysis.CSharp.CodeStyle" Version="4.12.0-3.24427.16">
<Dependency Name="Microsoft.CodeAnalysis.CSharp.CodeStyle" Version="4.12.0-3.24428.2">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>a3dbd808a5ee9b8010d38f37726eeb596e2f3aaa</Sha>
<Sha>8e5e9ad66e5a8137253bfb57448e0fef4d7a6e0f</Sha>
</Dependency>
<Dependency Name="Microsoft.CodeAnalysis.CSharp.Features" Version="4.12.0-3.24427.16">
<Dependency Name="Microsoft.CodeAnalysis.CSharp.Features" Version="4.12.0-3.24428.2">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>a3dbd808a5ee9b8010d38f37726eeb596e2f3aaa</Sha>
<Sha>8e5e9ad66e5a8137253bfb57448e0fef4d7a6e0f</Sha>
</Dependency>
<Dependency Name="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.12.0-3.24427.16">
<Dependency Name="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.12.0-3.24428.2">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>a3dbd808a5ee9b8010d38f37726eeb596e2f3aaa</Sha>
<Sha>8e5e9ad66e5a8137253bfb57448e0fef4d7a6e0f</Sha>
</Dependency>
<Dependency Name="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="4.12.0-3.24427.16">
<Dependency Name="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="4.12.0-3.24428.2">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>a3dbd808a5ee9b8010d38f37726eeb596e2f3aaa</Sha>
<Sha>8e5e9ad66e5a8137253bfb57448e0fef4d7a6e0f</Sha>
</Dependency>
<Dependency Name="Microsoft.AspNetCore.DeveloperCertificates.XPlat" Version="9.0.0-rc.1.24414.4">
<Uri>https://github.com/dotnet/aspnetcore</Uri>
Expand Down
17 changes: 9 additions & 8 deletions eng/Versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
compiler API targeted by analyzer assemblies. This is mostly an issue on source-build as
in that build mode analyzer assemblies always target the live compiler API. -->
<UsingToolMicrosoftNetCompilers Condition="'$(DotNetBuildSourceOnly)' == 'true'">true</UsingToolMicrosoftNetCompilers>
<UsingToolVSSDK>true</UsingToolVSSDK>
<MicrosoftIORedistPackageVersion>6.0.1</MicrosoftIORedistPackageVersion>
<FlagNetStandard1XDependencies Condition="'$(DotNetBuildSourceOnly)' == 'true'">true</FlagNetStandard1XDependencies>
</PropertyGroup>
Expand Down Expand Up @@ -238,14 +239,14 @@
</PropertyGroup>
<PropertyGroup>
<!-- Dependencies from https://github.com/dotnet/roslyn -->
<MicrosoftNetCompilersToolsetVersion>4.12.0-3.24427.16</MicrosoftNetCompilersToolsetVersion>
<MicrosoftNetCompilersToolsetFrameworkPackageVersion>4.12.0-3.24427.16</MicrosoftNetCompilersToolsetFrameworkPackageVersion>
<MicrosoftCodeAnalysisPackageVersion>4.12.0-3.24427.16</MicrosoftCodeAnalysisPackageVersion>
<MicrosoftCodeAnalysisCSharpPackageVersion>4.12.0-3.24427.16</MicrosoftCodeAnalysisCSharpPackageVersion>
<MicrosoftCodeAnalysisCSharpCodeStylePackageVersion>4.12.0-3.24427.16</MicrosoftCodeAnalysisCSharpCodeStylePackageVersion>
<MicrosoftCodeAnalysisCSharpFeaturesPackageVersion>4.12.0-3.24427.16</MicrosoftCodeAnalysisCSharpFeaturesPackageVersion>
<MicrosoftCodeAnalysisWorkspacesMSBuildPackageVersion>4.12.0-3.24427.16</MicrosoftCodeAnalysisWorkspacesMSBuildPackageVersion>
<MicrosoftCodeAnalysisCSharpWorkspacesPackageVersion>4.12.0-3.24427.16</MicrosoftCodeAnalysisCSharpWorkspacesPackageVersion>
<MicrosoftNetCompilersToolsetVersion>4.12.0-3.24428.2</MicrosoftNetCompilersToolsetVersion>
<MicrosoftNetCompilersToolsetFrameworkPackageVersion>4.12.0-3.24428.2</MicrosoftNetCompilersToolsetFrameworkPackageVersion>
<MicrosoftCodeAnalysisPackageVersion>4.12.0-3.24428.2</MicrosoftCodeAnalysisPackageVersion>
<MicrosoftCodeAnalysisCSharpPackageVersion>4.12.0-3.24428.2</MicrosoftCodeAnalysisCSharpPackageVersion>
<MicrosoftCodeAnalysisCSharpCodeStylePackageVersion>4.12.0-3.24428.2</MicrosoftCodeAnalysisCSharpCodeStylePackageVersion>
<MicrosoftCodeAnalysisCSharpFeaturesPackageVersion>4.12.0-3.24428.2</MicrosoftCodeAnalysisCSharpFeaturesPackageVersion>
<MicrosoftCodeAnalysisWorkspacesMSBuildPackageVersion>4.12.0-3.24428.2</MicrosoftCodeAnalysisWorkspacesMSBuildPackageVersion>
<MicrosoftCodeAnalysisCSharpWorkspacesPackageVersion>4.12.0-3.24428.2</MicrosoftCodeAnalysisCSharpWorkspacesPackageVersion>
</PropertyGroup>
<PropertyGroup>
<!-- Dependencies from https://github.com/aspnet/AspNetCore -->
Expand Down
14 changes: 14 additions & 0 deletions sdk.sln
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,10 @@ Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Microsoft.WebTools.AspireSe
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Net.Sdk.Compilers.Toolset", "src\Microsoft.Net.Sdk.Compilers.Toolset\Microsoft.Net.Sdk.Compilers.Toolset.csproj", "{FA579C03-2EB4-4D47-88EE-BFF339E96FAF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Net.Sdk.AnalyzerRedirecting", "src\Microsoft.Net.Sdk.AnalyzerRedirecting\Microsoft.Net.Sdk.AnalyzerRedirecting.csproj", "{27BBE29B-CE6F-401F-B3CF-B07DC556FAD1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Net.Sdk.AnalyzerRedirecting.Tests", "test\Microsoft.Net.Sdk.AnalyzerRedirecting.Tests\Microsoft.Net.Sdk.AnalyzerRedirecting.Tests.csproj", "{234BE46E-4AD6-485C-B0D3-E704EF504312}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -965,6 +969,14 @@ Global
{FA579C03-2EB4-4D47-88EE-BFF339E96FAF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FA579C03-2EB4-4D47-88EE-BFF339E96FAF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FA579C03-2EB4-4D47-88EE-BFF339E96FAF}.Release|Any CPU.Build.0 = Release|Any CPU
{27BBE29B-CE6F-401F-B3CF-B07DC556FAD1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{27BBE29B-CE6F-401F-B3CF-B07DC556FAD1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{27BBE29B-CE6F-401F-B3CF-B07DC556FAD1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{27BBE29B-CE6F-401F-B3CF-B07DC556FAD1}.Release|Any CPU.Build.0 = Release|Any CPU
{234BE46E-4AD6-485C-B0D3-E704EF504312}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{234BE46E-4AD6-485C-B0D3-E704EF504312}.Debug|Any CPU.Build.0 = Debug|Any CPU
{234BE46E-4AD6-485C-B0D3-E704EF504312}.Release|Any CPU.ActiveCfg = Release|Any CPU
{234BE46E-4AD6-485C-B0D3-E704EF504312}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -1143,6 +1155,8 @@ Global
{19014C60-F87C-4CC7-AC0F-C41B6126EBCE} = {71A9F549-0EB6-41F9-BC16-4A6C5007FC91}
{94C8526E-DCC2-442F-9868-3DD0BA2688BE} = {71A9F549-0EB6-41F9-BC16-4A6C5007FC91}
{FA579C03-2EB4-4D47-88EE-BFF339E96FAF} = {22AB674F-ED91-4FBC-BFEE-8A1E82F9F05E}
{27BBE29B-CE6F-401F-B3CF-B07DC556FAD1} = {22AB674F-ED91-4FBC-BFEE-8A1E82F9F05E}
{234BE46E-4AD6-485C-B0D3-E704EF504312} = {580D1AE7-AA8F-4912-8B76-105594E00B3B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {FB8F26CE-4DE6-433F-B32A-79183020BBD6}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
using Microsoft.VisualStudio.Shell;

namespace Microsoft.Net.Sdk.AnalyzerRedirecting;

[Guid("ef89a321-14da-4de4-8f71-9bf1feea15aa")]
public sealed class AnalyzerRedirectingPackage : AsyncPackage;
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<Nullable>enable</Nullable>

<IsShipping>false</IsShipping>
<ExcludeFromSourceBuild>true</ExcludeFromSourceBuild>

<!-- VSIX -->
<VSSDKTargetPlatformRegRootSuffix>RoslynDev</VSSDKTargetPlatformRegRootSuffix>
<GeneratePkgDefFile>true</GeneratePkgDefFile>
<IncludeAssemblyInVSIXContainer>true</IncludeAssemblyInVSIXContainer>
<IncludeDebugSymbolsInVSIXContainer>true</IncludeDebugSymbolsInVSIXContainer>
<IncludeDebugSymbolsInLocalVSIXDeployment>true</IncludeDebugSymbolsInLocalVSIXDeployment>
<IncludeCopyLocalReferencesInVSIXContainer>false</IncludeCopyLocalReferencesInVSIXContainer>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" />
<PackageReference Include="Microsoft.VisualStudio.Sdk" />
</ItemGroup>

<ItemGroup>
<InternalsVisibleTo Include="Microsoft.Net.Sdk.AnalyzerRedirecting.Tests" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
// 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 Microsoft.CodeAnalysis;

// 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;

[Export(typeof(IAnalyzerAssemblyRedirector))]
public sealed class SdkAnalyzerAssemblyRedirector : IAnalyzerAssemblyRedirector
{
private readonly string? _insertedAnalyzersDirectory;
private readonly Lazy<ImmutableDictionary<string, List<AnalyzerInfo>>> _analyzerMap;

[ImportingConstructor]
public SdkAnalyzerAssemblyRedirector()
: this(Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..", "..", "SDK", "RuntimeAnalyzers"))) { }

// Internal for testing.
internal SdkAnalyzerAssemblyRedirector(string? insertedAnalyzersDirectory)
{
_insertedAnalyzersDirectory = insertedAnalyzersDirectory;
_analyzerMap = new(CreateAnalyzerMap);
}

/// <summary>
/// Map from analyzer assembly name (file name without extension) to a list of matching analyzers.
/// </summary>
private ImmutableDictionary<string, List<AnalyzerInfo>> AnalyzerMap => _analyzerMap.Value;

private ImmutableDictionary<string, List<AnalyzerInfo>> CreateAnalyzerMap()
{
var builder = ImmutableDictionary.CreateBuilder<string, List<AnalyzerInfo>>(StringComparer.OrdinalIgnoreCase);

// Expects layout like:
// VsInstallDir\SDK\RuntimeAnalyzers\WindowsDesktopAnalyzers\8.0.8\analyzers\dotnet\System.Windows.Forms.Analyzers.dll
// ~~~~~~~~~~~~~~~~~~~~~~~ = topLevelDirectory
// ~~~~~ = versionDirectory
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ = analyzerPath

foreach (string topLevelDirectory in Directory.EnumerateDirectories(_insertedAnalyzersDirectory))
{
foreach (string versionDirectory in Directory.EnumerateDirectories(topLevelDirectory))
{
foreach (string analyzerPath in Directory.EnumerateFiles(versionDirectory, "*.dll", SearchOption.AllDirectories))
{
if (!analyzerPath.StartsWith(versionDirectory, StringComparison.OrdinalIgnoreCase))
{
continue;
}

string version = Path.GetFileName(versionDirectory);
string analyzerName = Path.GetFileNameWithoutExtension(analyzerPath);
string pathSuffix = analyzerPath.Substring(versionDirectory.Length + (EndsWithSlash(versionDirectory) ? 0 : 1));
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]);
}
}
}
}

return builder.ToImmutable();
}

public string? RedirectPath(string fullPath)
{
if (AnalyzerMap.TryGetValue(Path.GetFileNameWithoutExtension(fullPath), out var analyzers))
{
foreach (AnalyzerInfo analyzer in analyzers)
{
var directoryPath = Path.GetDirectoryName(fullPath);
if (endsWithIgnoringTrailingSlashes(directoryPath, analyzer.PathSuffix) &&
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
int index = directoryPath.LastIndexOf(pathSuffix, StringComparison.OrdinalIgnoreCase);
if (index < 0)
{
return false;
}
string directoryPathVersion = Path.GetFileName(Path.GetDirectoryName(directoryPath.Substring(0, index)));

return getMajorMinorPart(directoryPathVersion) == getMajorMinorPart(version);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is reasonable. I was imagining you'd use System.Version but I can see how that might complicate things. If you leave it this way you can avoid creating new strings by having a single method that compares the strings. Here's a sample

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also System.Version cannot parse versions with a hyphen it seems.

}

static string getMajorMinorPart(string version)
{
int firstDotIndex = version.IndexOf('.');
if (firstDotIndex < 0)
{
return version;
}

int secondDotIndex = version.IndexOf('.', firstDotIndex + 1);
if (secondDotIndex < 0)
{
return version;
}

return version.Substring(0, secondDotIndex);
}

static bool endsWithIgnoringTrailingSlashes(string s, string suffix)
{
var sEndsWithSlash = EndsWithSlash(s);
var suffixEndsWithSlash = EndsWithSlash(suffix);
var index = s.LastIndexOf(suffix, StringComparison.OrdinalIgnoreCase);
return index >= 0 && index + suffix.Length - (suffixEndsWithSlash ? 1 : 0) == s.Length - (sEndsWithSlash ? 1 : 0);
}
}

private static bool EndsWithSlash(string s) => !string.IsNullOrEmpty(s) && s[s.Length - 1] is '/' or '\\';
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011">
<Metadata>
<Identity Id="a39fdd10-6d59-4dca-b205-053f23b2afea" Version="|%CurrentProject%;GetVsixVersion|" Language="en-US" Publisher="Microsoft" />
<DisplayName>.NET SDK Analyzer Redirecting</DisplayName>
<Description xml:space="preserve">.NET SDK Analyzer Redirecting Package.</Description>
</Metadata>
<Dependencies>
<Dependency Id="Microsoft.Framework.NDP" DisplayName="Microsoft .NET Framework" d:Source="Manual" Version="[4.7.2,)" />
</Dependencies>
<Assets>
<Asset Type="Microsoft.VisualStudio.MefComponent" Path="|%CurrentProject%|" d:Source="Project" d:ProjectName="%CurrentProject%" />
<Asset Type="Microsoft.VisualStudio.VsPackage" Path="|%CurrentProject%;PkgdefProjectOutputGroup|" d:Source="Project" d:ProjectName="%CurrentProject%" />
</Assets>
<Prerequisites>
<Prerequisite Id="Microsoft.VisualStudio.Component.CoreEditor" Version="[17.0,18.0)" DisplayName="Visual Studio core editor" />
</Prerequisites>
<Installation Experimental="true">
<InstallationTarget Id="Microsoft.VisualStudio.Pro" Version="[17.0,18.0)">
<ProductArchitecture>amd64</ProductArchitecture>
</InstallationTarget>
</Installation>
</PackageManifest>
Loading