@@ -5,7 +5,6 @@ namespace StyleCop.Analyzers
55{
66 using System ;
77 using System . Collections . Concurrent ;
8- using System . Runtime . CompilerServices ;
98 using System . Threading ;
109 using Microsoft . CodeAnalysis ;
1110 using Microsoft . CodeAnalysis . Diagnostics ;
@@ -22,8 +21,8 @@ internal static class AnalyzerExtensions
2221 /// This allows many analyzers that run on every token in the file to avoid checking
2322 /// the same state in the document repeatedly.
2423 /// </remarks>
25- private static readonly ConditionalWeakTable < Compilation , ConcurrentDictionary < SyntaxTree , bool > > GeneratedHeaderCache
26- = new ConditionalWeakTable < Compilation , ConcurrentDictionary < SyntaxTree , bool > > ( ) ;
24+ private static Tuple < WeakReference < Compilation > , ConcurrentDictionary < SyntaxTree , bool > > generatedHeaderCache
25+ = Tuple . Create ( new WeakReference < Compilation > ( null ) , default ( ConcurrentDictionary < SyntaxTree , bool > ) ) ;
2726
2827 /// <summary>
2928 /// Register an action to be executed at completion of parsing of a code document. A syntax tree action reports
@@ -35,7 +34,7 @@ private static readonly ConditionalWeakTable<Compilation, ConcurrentDictionary<S
3534 public static void RegisterSyntaxTreeActionHonorExclusions ( this CompilationStartAnalysisContext context , Action < SyntaxTreeAnalysisContext > action )
3635 {
3736 Compilation compilation = context . Compilation ;
38- ConcurrentDictionary < SyntaxTree , bool > cache = GeneratedHeaderCache . GetOrCreateValue ( compilation ) ;
37+ ConcurrentDictionary < SyntaxTree , bool > cache = GetOrCreateGeneratedDocumentCache ( compilation ) ;
3938
4039 context . RegisterSyntaxTreeAction (
4140 c =>
@@ -53,6 +52,40 @@ public static void RegisterSyntaxTreeActionHonorExclusions(this CompilationStart
5352 } ) ;
5453 }
5554
55+ /// <summary>
56+ /// Gets or creates a cache which can be used with <see cref="GeneratedCodeAnalysisExtensions"/> methods to
57+ /// efficiently determine whether or not a source file is considered generated.
58+ /// </summary>
59+ /// <param name="compilation">The compilation which the cache applies to.</param>
60+ /// <returns>A cache which tracks the syntax trees in a compilation which are considered generated.</returns>
61+ public static ConcurrentDictionary < SyntaxTree , bool > GetOrCreateGeneratedDocumentCache ( this Compilation compilation )
62+ {
63+ var headerCache = generatedHeaderCache ;
64+
65+ Compilation cachedCompilation ;
66+ if ( ! headerCache . Item1 . TryGetTarget ( out cachedCompilation ) || cachedCompilation != compilation )
67+ {
68+ var replacementCache = Tuple . Create ( new WeakReference < Compilation > ( compilation ) , new ConcurrentDictionary < SyntaxTree , bool > ( ) ) ;
69+ while ( true )
70+ {
71+ var prior = Interlocked . CompareExchange ( ref generatedHeaderCache , replacementCache , headerCache ) ;
72+ if ( prior == headerCache )
73+ {
74+ headerCache = replacementCache ;
75+ break ;
76+ }
77+
78+ headerCache = prior ;
79+ if ( headerCache . Item1 . TryGetTarget ( out cachedCompilation ) && cachedCompilation == compilation )
80+ {
81+ break ;
82+ }
83+ }
84+ }
85+
86+ return headerCache . Item2 ;
87+ }
88+
5689 /// <summary>
5790 /// Register an action to be executed at completion of semantic analysis of a <see cref="SyntaxNode"/> with an
5891 /// appropriate kind. A syntax node action can report diagnostics about a <see cref="SyntaxNode"/>, and can also
@@ -70,7 +103,7 @@ public static void RegisterSyntaxNodeActionHonorExclusions<TLanguageKindEnum>(th
70103 where TLanguageKindEnum : struct
71104 {
72105 Compilation compilation = context . Compilation ;
73- ConcurrentDictionary < SyntaxTree , bool > cache = GeneratedHeaderCache . GetOrCreateValue ( compilation ) ;
106+ ConcurrentDictionary < SyntaxTree , bool > cache = GetOrCreateGeneratedDocumentCache ( compilation ) ;
74107
75108 context . RegisterSyntaxNodeAction (
76109 c =>
@@ -88,36 +121,5 @@ public static void RegisterSyntaxNodeActionHonorExclusions<TLanguageKindEnum>(th
88121 } ,
89122 syntaxKinds ) ;
90123 }
91-
92- /// <summary>
93- /// Checks whether the given document is auto generated by a tool (based on filename or comment header).
94- /// </summary>
95- /// <remarks>
96- /// <para>The exact conditions used to identify generated code are subject to change in future releases. The
97- /// current algorithm uses the following checks.</para>
98- /// <para>Code is considered generated if it meets any of the following conditions.</para>
99- /// <list type="bullet">
100- /// <item>The code is contained in a file which starts with a comment containing the text
101- /// <c><auto-generated</c>.</item>
102- /// <item>The code is contained in a file with a name matching certain patterns (case-insensitive):
103- /// <list type="bullet">
104- /// <item>*.designer.cs</item>
105- /// </list>
106- /// </item>
107- /// </list>
108- /// </remarks>
109- /// <param name="tree">The syntax tree to examine.</param>
110- /// <param name="compilation">The <see cref="Compilation"/> containing the specified <paramref name="tree"/>.</param>
111- /// <param name="cancellationToken">The <see cref="CancellationToken"/> that the task will observe.</param>
112- /// <returns>
113- /// <para><see langword="true"/> if <paramref name="tree"/> is located in generated code; otherwise,
114- /// <see langword="false"/>. If <paramref name="tree"/> is <see langword="null"/>, this method returns
115- /// <see langword="false"/>.</para>
116- /// </returns>
117- public static bool IsGeneratedDocument ( this SyntaxTree tree , Compilation compilation , CancellationToken cancellationToken )
118- {
119- ConcurrentDictionary < SyntaxTree , bool > cache = GeneratedHeaderCache . GetOrCreateValue ( compilation ) ;
120- return tree . IsGeneratedDocument ( cache , cancellationToken ) ;
121- }
122124 }
123125}
0 commit comments