Skip to content

Commit d4de0b6

Browse files
committed
Merge pull request #1805 from sharwell/cache-settings
StyleCopSettings cache
2 parents 5ad4358 + 436d870 commit d4de0b6

8 files changed

Lines changed: 266 additions & 174 deletions

StyleCop.Analyzers/StyleCop.Analyzers/AnalyzerExtensions.cs

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ namespace StyleCop.Analyzers
66
using System;
77
using System.Collections.Concurrent;
88
using System.Collections.Immutable;
9+
using System.Runtime.CompilerServices;
910
using System.Threading;
1011
using Microsoft.CodeAnalysis;
1112
using Microsoft.CodeAnalysis.Diagnostics;
13+
using Settings.ObjectModel;
1214

1315
/// <summary>
1416
/// Provides extension methods to deal for analyzers.
@@ -25,6 +27,9 @@ internal static class AnalyzerExtensions
2527
private static Tuple<WeakReference<Compilation>, ConcurrentDictionary<SyntaxTree, bool>> generatedHeaderCache
2628
= Tuple.Create(new WeakReference<Compilation>(null), default(ConcurrentDictionary<SyntaxTree, bool>));
2729

30+
private static Tuple<WeakReference<Compilation>, StrongBox<StyleCopSettings>> settingsCache
31+
= Tuple.Create(new WeakReference<Compilation>(null), default(StrongBox<StyleCopSettings>));
32+
2833
/// <summary>
2934
/// Register an action to be executed at completion of parsing of a code document. A syntax tree action reports
3035
/// diagnostics about the <see cref="SyntaxTree"/> of a document.
@@ -53,6 +58,43 @@ public static void RegisterSyntaxTreeActionHonorExclusions(this CompilationStart
5358
});
5459
}
5560

61+
/// <summary>
62+
/// Register an action to be executed at completion of parsing of a code document. A syntax tree action reports
63+
/// diagnostics about the <see cref="SyntaxTree"/> of a document.
64+
/// </summary>
65+
/// <remarks>This method honors exclusions.</remarks>
66+
/// <param name="context">The analysis context.</param>
67+
/// <param name="action">Action to be executed at completion of parsing of a document.</param>
68+
public static void RegisterSyntaxTreeActionHonorExclusions(this CompilationStartAnalysisContext context, Action<SyntaxTreeAnalysisContext, StyleCopSettings> action)
69+
{
70+
Compilation compilation = context.Compilation;
71+
ConcurrentDictionary<SyntaxTree, bool> cache = GetOrCreateGeneratedDocumentCache(compilation);
72+
StrongBox<StyleCopSettings> settingsCache = GetOrCreateStyleCopSettingsCache(compilation);
73+
74+
context.RegisterSyntaxTreeAction(
75+
c =>
76+
{
77+
if (c.IsGeneratedDocument(cache))
78+
{
79+
return;
80+
}
81+
82+
// Honor the containing document item's ExcludeFromStylecop=True
83+
// MSBuild metadata, if analyzers have access to it.
84+
//// TODO: code here
85+
86+
StyleCopSettings settings = settingsCache.Value;
87+
if (settings == null)
88+
{
89+
StyleCopSettings updatedSettings = SettingsHelper.GetStyleCopSettings(c.Options, c.CancellationToken);
90+
StyleCopSettings previous = Interlocked.CompareExchange(ref settingsCache.Value, updatedSettings, null);
91+
settings = previous ?? updatedSettings;
92+
}
93+
94+
action(c, settings);
95+
});
96+
}
97+
5698
/// <summary>
5799
/// Gets or creates a cache which can be used with <see cref="GeneratedCodeAnalysisExtensions"/> methods to
58100
/// efficiently determine whether or not a source file is considered generated.
@@ -87,6 +129,40 @@ public static ConcurrentDictionary<SyntaxTree, bool> GetOrCreateGeneratedDocumen
87129
return headerCache.Item2;
88130
}
89131

132+
/// <summary>
133+
/// Gets a <see cref="StrongBox{T}"/> which can store a <see cref="StyleCopSettings"/> instance to improve
134+
/// efficiency across multiple analyzers which examine settings.
135+
/// </summary>
136+
/// <param name="compilation">The compilation which the cache applies to.</param>
137+
/// <returns>A <see cref="StrongBox{T}"/> which can store a <see cref="StyleCopSettings"/> instance.</returns>
138+
public static StrongBox<StyleCopSettings> GetOrCreateStyleCopSettingsCache(this Compilation compilation)
139+
{
140+
var currentSettingsCache = settingsCache;
141+
142+
Compilation cachedCompilation;
143+
if (!currentSettingsCache.Item1.TryGetTarget(out cachedCompilation) || cachedCompilation != compilation)
144+
{
145+
var replacementCache = Tuple.Create(new WeakReference<Compilation>(compilation), new StrongBox<StyleCopSettings>(null));
146+
while (true)
147+
{
148+
var prior = Interlocked.CompareExchange(ref settingsCache, replacementCache, currentSettingsCache);
149+
if (prior == currentSettingsCache)
150+
{
151+
currentSettingsCache = replacementCache;
152+
break;
153+
}
154+
155+
currentSettingsCache = prior;
156+
if (currentSettingsCache.Item1.TryGetTarget(out cachedCompilation) && cachedCompilation == compilation)
157+
{
158+
break;
159+
}
160+
}
161+
}
162+
163+
return currentSettingsCache.Item2;
164+
}
165+
90166
/// <summary>
91167
/// Register an action to be executed at completion of semantic analysis of a <see cref="SyntaxNode"/> with an
92168
/// appropriate kind. A syntax node action can report diagnostics about a <see cref="SyntaxNode"/>, and can also
@@ -106,6 +182,25 @@ public static void RegisterSyntaxNodeActionHonorExclusions<TLanguageKindEnum>(th
106182
context.RegisterSyntaxNodeActionHonorExclusions(action, LanguageKindArrays<TLanguageKindEnum>.GetOrCreateArray(syntaxKind));
107183
}
108184

185+
/// <summary>
186+
/// Register an action to be executed at completion of semantic analysis of a <see cref="SyntaxNode"/> with an
187+
/// appropriate kind. A syntax node action can report diagnostics about a <see cref="SyntaxNode"/>, and can also
188+
/// collect state information to be used by other syntax node actions or code block end actions.
189+
/// </summary>
190+
/// <remarks>This method honors exclusions.</remarks>
191+
/// <param name="context">Action will be executed only if the kind of a <see cref="SyntaxNode"/> matches
192+
/// <paramref name="syntaxKind"/>.</param>
193+
/// <param name="action">Action to be executed at completion of semantic analysis of a
194+
/// <see cref="SyntaxNode"/>.</param>
195+
/// <param name="syntaxKind">The kind of syntax that should be analyzed.</param>
196+
/// <typeparam name="TLanguageKindEnum">Enum type giving the syntax node kinds of the source language for which
197+
/// the action applies.</typeparam>
198+
public static void RegisterSyntaxNodeActionHonorExclusions<TLanguageKindEnum>(this CompilationStartAnalysisContext context, Action<SyntaxNodeAnalysisContext, StyleCopSettings> action, TLanguageKindEnum syntaxKind)
199+
where TLanguageKindEnum : struct
200+
{
201+
context.RegisterSyntaxNodeActionHonorExclusions(action, LanguageKindArrays<TLanguageKindEnum>.GetOrCreateArray(syntaxKind));
202+
}
203+
109204
/// <summary>
110205
/// Register an action to be executed at completion of semantic analysis of a <see cref="SyntaxNode"/> with an
111206
/// appropriate kind. A syntax node action can report diagnostics about a <see cref="SyntaxNode"/>, and can also
@@ -142,6 +237,51 @@ public static void RegisterSyntaxNodeActionHonorExclusions<TLanguageKindEnum>(th
142237
syntaxKinds);
143238
}
144239

240+
/// <summary>
241+
/// Register an action to be executed at completion of semantic analysis of a <see cref="SyntaxNode"/> with an
242+
/// appropriate kind. A syntax node action can report diagnostics about a <see cref="SyntaxNode"/>, and can also
243+
/// collect state information to be used by other syntax node actions or code block end actions.
244+
/// </summary>
245+
/// <remarks>This method honors exclusions.</remarks>
246+
/// <param name="context">Action will be executed only if the kind of a <see cref="SyntaxNode"/> matches one of
247+
/// the <paramref name="syntaxKinds"/> values.</param>
248+
/// <param name="action">Action to be executed at completion of semantic analysis of a
249+
/// <see cref="SyntaxNode"/>.</param>
250+
/// <param name="syntaxKinds">The kinds of syntax that should be analyzed.</param>
251+
/// <typeparam name="TLanguageKindEnum">Enum type giving the syntax node kinds of the source language for which
252+
/// the action applies.</typeparam>
253+
public static void RegisterSyntaxNodeActionHonorExclusions<TLanguageKindEnum>(this CompilationStartAnalysisContext context, Action<SyntaxNodeAnalysisContext, StyleCopSettings> action, ImmutableArray<TLanguageKindEnum> syntaxKinds)
254+
where TLanguageKindEnum : struct
255+
{
256+
Compilation compilation = context.Compilation;
257+
ConcurrentDictionary<SyntaxTree, bool> cache = GetOrCreateGeneratedDocumentCache(compilation);
258+
StrongBox<StyleCopSettings> settingsCache = GetOrCreateStyleCopSettingsCache(compilation);
259+
260+
context.RegisterSyntaxNodeAction(
261+
c =>
262+
{
263+
if (c.IsGenerated(cache))
264+
{
265+
return;
266+
}
267+
268+
// Honor the containing document item's ExcludeFromStylecop=True
269+
// MSBuild metadata, if analyzers have access to it.
270+
//// TODO: code here
271+
272+
StyleCopSettings settings = settingsCache.Value;
273+
if (settings == null)
274+
{
275+
StyleCopSettings updatedSettings = SettingsHelper.GetStyleCopSettings(c.Options, c.CancellationToken);
276+
StyleCopSettings previous = Interlocked.CompareExchange(ref settingsCache.Value, updatedSettings, null);
277+
settings = previous ?? updatedSettings;
278+
}
279+
280+
action(c, settings);
281+
},
282+
syntaxKinds);
283+
}
284+
145285
private static class LanguageKindArrays<TLanguageKindEnum>
146286
where TLanguageKindEnum : struct
147287
{

0 commit comments

Comments
 (0)