Skip to content

Commit dab46df

Browse files
committed
Add AnalyzerLoader and remove AnalyzerAssemblyList
1 parent 6aab001 commit dab46df

File tree

8 files changed

+262
-297
lines changed

8 files changed

+262
-297
lines changed

src/CommandLine/Commands/AnalyzeCommand.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,22 @@ public override async Task<AnalyzeCommandResult> ExecuteAsync(ProjectOrSolution
4545

4646
CultureInfo culture = (Options.Culture != null) ? CultureInfo.GetCultureInfo(Options.Culture) : null;
4747

48+
var analyzerLoader = new AnalyzerLoader(analyzerAssemblies, codeAnalyzerOptions);
49+
50+
analyzerLoader.AnalyzerAssemblyAdded += (sender, args) =>
51+
{
52+
AnalyzerAssembly analyzerAssembly = args.AnalyzerAssembly;
53+
54+
if (analyzerAssembly.Name.EndsWith(".Analyzers")
55+
|| analyzerAssembly.HasAnalyzers
56+
|| analyzerAssembly.HasFixers)
57+
{
58+
WriteLine($"Add analyzer assembly '{analyzerAssembly.FullName}'", ConsoleColors.DarkGray, Verbosity.Detailed);
59+
}
60+
};
61+
4862
var codeAnalyzer = new CodeAnalyzer(
49-
analyzerAssemblies: analyzerAssemblies,
63+
analyzerLoader: analyzerLoader,
5064
formatProvider: culture,
5165
options: codeAnalyzerOptions);
5266

src/CommandLine/Commands/FixCommand.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,23 @@ private static async Task<FixCommandResult> FixAsync(
125125

126126
CodeFixer GetCodeFixer(Solution solution)
127127
{
128+
var analyzerLoader = new AnalyzerLoader(analyzerAssemblies, codeFixerOptions);
129+
130+
analyzerLoader.AnalyzerAssemblyAdded += (sender, args) =>
131+
{
132+
AnalyzerAssembly analyzerAssembly = args.AnalyzerAssembly;
133+
134+
if (analyzerAssembly.Name.EndsWith(".Analyzers")
135+
|| analyzerAssembly.HasAnalyzers
136+
|| analyzerAssembly.HasFixers)
137+
{
138+
WriteLine($"Add analyzer assembly '{analyzerAssembly.FullName}'", ConsoleColors.DarkGray, Verbosity.Detailed);
139+
}
140+
};
141+
128142
return new CodeFixer(
129143
solution,
130-
analyzerAssemblies: analyzerAssemblies,
144+
analyzerLoader: analyzerLoader,
131145
formatProvider: formatProvider,
132146
options: codeFixerOptions);
133147
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright (c) Josef Pihrt. All rights reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
2+
3+
using System;
4+
5+
namespace Roslynator
6+
{
7+
internal class AnalyzerAssemblyEventArgs : EventArgs
8+
{
9+
public AnalyzerAssemblyEventArgs(AnalyzerAssembly analyzerAssembly)
10+
{
11+
AnalyzerAssembly = analyzerAssembly;
12+
}
13+
14+
public AnalyzerAssembly AnalyzerAssembly { get; }
15+
}
16+
}

src/Workspaces.Core/AnalyzerAssemblyList.cs

Lines changed: 0 additions & 110 deletions
This file was deleted.
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
// Copyright (c) Josef Pihrt. All rights reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
2+
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Collections.Immutable;
6+
using System.Linq;
7+
using System.Reflection;
8+
using Microsoft.CodeAnalysis;
9+
using Microsoft.CodeAnalysis.CodeFixes;
10+
using Microsoft.CodeAnalysis.Diagnostics;
11+
12+
namespace Roslynator
13+
{
14+
internal class AnalyzerLoader
15+
{
16+
private readonly Dictionary<string, AnalyzerAssembly> _cache = new Dictionary<string, AnalyzerAssembly>();
17+
private readonly Dictionary<string, AnalyzerAssembly> _defaultAssemblies = new Dictionary<string, AnalyzerAssembly>();
18+
private readonly Dictionary<string, ImmutableArray<DiagnosticAnalyzer>> _defaultAnalyzers = new Dictionary<string, ImmutableArray<DiagnosticAnalyzer>>();
19+
private readonly Dictionary<string, ImmutableArray<CodeFixProvider>> _defaultFixers = new Dictionary<string, ImmutableArray<CodeFixProvider>>();
20+
21+
public AnalyzerLoader(IEnumerable<AnalyzerAssembly> defaultAssemblies, CodeAnalysisOptions options)
22+
{
23+
DefaultAssemblies = ImmutableArray<AnalyzerAssembly>.Empty;
24+
25+
if (defaultAssemblies != null)
26+
{
27+
foreach (AnalyzerAssembly analyzerAssembly in defaultAssemblies)
28+
{
29+
if (!_defaultAssemblies.ContainsKey(analyzerAssembly.FullName))
30+
{
31+
_defaultAssemblies.Add(analyzerAssembly.FullName, analyzerAssembly);
32+
OnAnalyzerAssemblyAdded(new AnalyzerAssemblyEventArgs(analyzerAssembly));
33+
}
34+
}
35+
36+
DefaultAssemblies = _defaultAssemblies.Select(f => f.Value).ToImmutableArray();
37+
}
38+
39+
Options = options;
40+
}
41+
42+
public ImmutableArray<AnalyzerAssembly> DefaultAssemblies { get; }
43+
44+
public CodeAnalysisOptions Options { get; }
45+
46+
public event EventHandler<AnalyzerAssemblyEventArgs> AnalyzerAssemblyAdded;
47+
48+
protected virtual void OnAnalyzerAssemblyAdded(AnalyzerAssemblyEventArgs e)
49+
{
50+
AnalyzerAssemblyAdded?.Invoke(this, e);
51+
}
52+
53+
public ImmutableArray<DiagnosticAnalyzer> GetAnalyzers(Project project)
54+
{
55+
(ImmutableArray<DiagnosticAnalyzer> analyzers, ImmutableArray<CodeFixProvider> _) = GetAnalyzersAndFixers(project: project, loadFixers: false);
56+
57+
return analyzers;
58+
}
59+
60+
public (ImmutableArray<DiagnosticAnalyzer> analyzers, ImmutableArray<CodeFixProvider> fixers) GetAnalyzersAndFixers(Project project)
61+
{
62+
return GetAnalyzersAndFixers(project: project, loadFixers: true);
63+
}
64+
65+
private (ImmutableArray<DiagnosticAnalyzer> analyzers, ImmutableArray<CodeFixProvider> fixers) GetAnalyzersAndFixers(
66+
Project project,
67+
bool loadFixers = true)
68+
{
69+
string language = project.Language;
70+
71+
ImmutableArray<DiagnosticAnalyzer>.Builder analyzers = ImmutableArray.CreateBuilder<DiagnosticAnalyzer>();
72+
ImmutableArray<CodeFixProvider>.Builder fixers = ImmutableArray.CreateBuilder<CodeFixProvider>();
73+
74+
if (_defaultAnalyzers.TryGetValue(language, out ImmutableArray<DiagnosticAnalyzer> defaultAnalyzers))
75+
{
76+
analyzers.AddRange(defaultAnalyzers);
77+
}
78+
else
79+
{
80+
foreach (AnalyzerAssembly analyzerAssembly in DefaultAssemblies)
81+
LoadAnalyzers(analyzerAssembly, language, ref analyzers);
82+
83+
_defaultAnalyzers.Add(language, analyzers.ToImmutableArray());
84+
}
85+
86+
if (loadFixers)
87+
{
88+
if (_defaultFixers.TryGetValue(language, out ImmutableArray<CodeFixProvider> defaultFixers))
89+
{
90+
fixers.AddRange(defaultFixers);
91+
}
92+
else
93+
{
94+
foreach (AnalyzerAssembly analyzerAssembly in DefaultAssemblies)
95+
LoadFixers(analyzerAssembly, language, ref fixers);
96+
97+
_defaultFixers.Add(language, fixers.ToImmutableArray());
98+
}
99+
}
100+
101+
if (!Options.IgnoreAnalyzerReferences)
102+
{
103+
foreach (Assembly assembly in project.AnalyzerReferences
104+
.Distinct()
105+
.OfType<AnalyzerFileReference>()
106+
.Select(f => f.GetAssembly())
107+
.Where(f => !_defaultAssemblies.ContainsKey(f.FullName)))
108+
{
109+
if (!_cache.TryGetValue(assembly.FullName, out AnalyzerAssembly analyzerAssembly))
110+
{
111+
analyzerAssembly = AnalyzerAssembly.Load(assembly);
112+
_cache.Add(analyzerAssembly.FullName, analyzerAssembly);
113+
114+
OnAnalyzerAssemblyAdded(new AnalyzerAssemblyEventArgs(analyzerAssembly));
115+
}
116+
117+
LoadAnalyzers(analyzerAssembly, language, ref analyzers);
118+
119+
if (loadFixers)
120+
LoadFixers(analyzerAssembly, language, ref fixers);
121+
}
122+
}
123+
124+
return (analyzers.ToImmutableArray(), fixers.ToImmutableArray());
125+
}
126+
127+
private void LoadAnalyzers(AnalyzerAssembly analyzerAssembly, string language, ref ImmutableArray<DiagnosticAnalyzer>.Builder builder)
128+
{
129+
if (analyzerAssembly.AnalyzersByLanguage.TryGetValue(language, out ImmutableArray<DiagnosticAnalyzer> analyzers))
130+
{
131+
foreach (DiagnosticAnalyzer analyzer in analyzers)
132+
{
133+
if (ShouldIncludeAnalyzer(analyzer))
134+
builder.Add(analyzer);
135+
}
136+
}
137+
}
138+
139+
private bool ShouldIncludeAnalyzer(DiagnosticAnalyzer analyzer)
140+
{
141+
if (Options.SupportedDiagnosticIds.Count > 0)
142+
{
143+
foreach (DiagnosticDescriptor supportedDiagnostic in analyzer.SupportedDiagnostics)
144+
{
145+
if (Options.SupportedDiagnosticIds.Contains(supportedDiagnostic.Id))
146+
return true;
147+
}
148+
149+
return false;
150+
}
151+
else if (Options.IgnoredDiagnosticIds.Count > 0)
152+
{
153+
foreach (DiagnosticDescriptor supportedDiagnostic in analyzer.SupportedDiagnostics)
154+
{
155+
if (!Options.IgnoredDiagnosticIds.Contains(supportedDiagnostic.Id))
156+
return true;
157+
}
158+
159+
return false;
160+
}
161+
else
162+
{
163+
return true;
164+
}
165+
}
166+
167+
private void LoadFixers(AnalyzerAssembly analyzerAssembly, string language, ref ImmutableArray<CodeFixProvider>.Builder builder)
168+
{
169+
if (analyzerAssembly.FixersByLanguage.TryGetValue(language, out ImmutableArray<CodeFixProvider> fixers))
170+
{
171+
foreach (CodeFixProvider fixer in fixers)
172+
{
173+
if (ShouldIncludeFixer(fixer))
174+
builder.Add(fixer);
175+
}
176+
}
177+
}
178+
179+
private bool ShouldIncludeFixer(CodeFixProvider fixer)
180+
{
181+
if (Options.SupportedDiagnosticIds.Count > 0)
182+
{
183+
foreach (string fixableDiagnosticId in fixer.FixableDiagnosticIds)
184+
{
185+
if (Options.SupportedDiagnosticIds.Contains(fixableDiagnosticId))
186+
return true;
187+
}
188+
189+
return false;
190+
}
191+
else if (Options.IgnoredDiagnosticIds.Count > 0)
192+
{
193+
foreach (string fixableDiagnosticId in fixer.FixableDiagnosticIds)
194+
{
195+
if (!Options.IgnoredDiagnosticIds.Contains(fixableDiagnosticId))
196+
return true;
197+
}
198+
199+
return false;
200+
}
201+
else
202+
{
203+
return true;
204+
}
205+
}
206+
}
207+
}

0 commit comments

Comments
 (0)