From ae02df0074b3d05f63d5f4667e367fad99613c7d Mon Sep 17 00:00:00 2001 From: Joey Robichaud Date: Mon, 8 Feb 2021 12:00:03 -0800 Subject: [PATCH] Search all Analyzer assembly paths when resolving dependencies --- .../AnalyzerReferenceInformationProvider.cs | 25 +++++++--- .../ThirdPartyAnalyzerFormatterTests.cs | 49 ++++++++++++++++++- .../analyzer_project/analyzer_project.csproj | 1 + 3 files changed, 67 insertions(+), 8 deletions(-) diff --git a/src/Analyzers/AnalyzerReferenceInformationProvider.cs b/src/Analyzers/AnalyzerReferenceInformationProvider.cs index eaae51abed..95d54ab68d 100644 --- a/src/Analyzers/AnalyzerReferenceInformationProvider.cs +++ b/src/Analyzers/AnalyzerReferenceInformationProvider.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. +using System.Collections.Generic; using System.Collections.Immutable; using System.IO; using System.Linq; @@ -43,7 +44,9 @@ private AnalyzersAndFixers GetAnalyzersAndFixers(Project project) try { - context.AssemblyFolderPath = Path.GetDirectoryName(path); + // Until types are realized not all dependencies are loaded. Track each + // assembly's path as a search path when resolving dependencies. + context.AddSearchPath(Path.GetDirectoryName(path)); // First try loading the assembly from disk. return context.LoadFromAssemblyPath(path); @@ -58,7 +61,12 @@ private AnalyzersAndFixers GetAnalyzersAndFixers(Project project) internal sealed class AnalyzerLoadContext : AssemblyLoadContext { - internal string AssemblyFolderPath { get; set; } = string.Empty; + internal readonly HashSet _searchPaths = new HashSet(); + + internal void AddSearchPath(string path) + { + _searchPaths.Add(path); + } protected override Assembly Load(AssemblyName assemblyName) { @@ -68,12 +76,15 @@ protected override Assembly Load(AssemblyName assemblyName) try { - // Search for assembly based on assembly name and culture within the analyzer folder. - var assembly = AssemblyResolver.TryResolveAssemblyFromPaths(this, assemblyName, AssemblyFolderPath); - - if (assembly != null) + foreach (var searchPath in _searchPaths) { - return assembly; + // Search for assembly based on assembly name and culture within the analyzer folder. + var assembly = AssemblyResolver.TryResolveAssemblyFromPaths(this, assemblyName, searchPath); + + if (assembly != null) + { + return assembly; + } } } catch { } diff --git a/tests/Analyzers/ThirdPartyAnalyzerFormatterTests.cs b/tests/Analyzers/ThirdPartyAnalyzerFormatterTests.cs index 3d463bd730..f430e9d6af 100644 --- a/tests/Analyzers/ThirdPartyAnalyzerFormatterTests.cs +++ b/tests/Analyzers/ThirdPartyAnalyzerFormatterTests.cs @@ -120,7 +120,7 @@ void M() } [Fact] - public async Task TestIDisposableAnalyzer_Loads() + public async Task TestIDisposableAnalyzer_AddsUsing() { var analyzerReferences = GetAnalyzerReferences("IDisposable"); @@ -141,6 +141,53 @@ void M() var expectedCode = @" using System.IO; +class C +{ + void M() + { + using (var stream = File.OpenRead(string.Empty)) + { + var b = stream.ReadByte(); + } + } +} +"; + + var editorConfig = new Dictionary() + { + // Turn off all diagnostics analyzers + ["dotnet_analyzer_diagnostic.severity"] = "none", + + // Prefer using. IDISP017 + ["dotnet_diagnostic.IDISP017.severity"] = "error", + }; + + await AssertCodeChangedAsync(testCode, expectedCode, editorConfig, fixCategory: FixCategory.Analyzers, analyzerReferences: analyzerReferences); + } + + [Fact] + public async Task TestLoadingAllAnalyzers_LoadsDependenciesFromAllSearchPaths() + { + // Loads all analyzer references. + var analyzerReferences = _analyzerReferencesProject.AnalyzerReferences; + + var testCode = @" +using System.IO; + +class C +{ + void M() + { + var stream = File.OpenRead(string.Empty); + var b = stream.ReadByte(); + stream.Dispose(); + } +} +"; + + var expectedCode = @" +using System.IO; + class C { void M() diff --git a/tests/projects/for_analyzer_formatter/analyzer_project/analyzer_project.csproj b/tests/projects/for_analyzer_formatter/analyzer_project/analyzer_project.csproj index b106a32018..ad9d39ed6d 100644 --- a/tests/projects/for_analyzer_formatter/analyzer_project/analyzer_project.csproj +++ b/tests/projects/for_analyzer_formatter/analyzer_project/analyzer_project.csproj @@ -9,6 +9,7 @@ all runtime; build; native; contentfiles; analyzers +