@@ -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