1- // This file originally obtained from
2- // https://github.com/code-cracker/code-cracker/blob/08c1a01337964924eeed12be8b14c8ce8ec6b626/src/Common/CodeCracker.Common/Extensions/AnalyzerExtensions.cs
3- // It is subject to the Apache License 2.0
4- // This file has been modified since obtaining it from its original source.
5-
6- namespace AsyncUsageAnalyzers
1+ namespace AsyncUsageAnalyzers
72{
83 using System ;
4+ using System . Collections . Concurrent ;
5+ using System . Collections . Immutable ;
6+ using System . Threading ;
7+ using Microsoft . CodeAnalysis ;
98 using Microsoft . CodeAnalysis . Diagnostics ;
109
10+ /// <summary>
11+ /// Provides extension methods to deal for analyzers.
12+ /// </summary>
1113 internal static class AnalyzerExtensions
1214 {
13- internal static void RegisterSyntaxTreeActionHonorExclusions ( this AnalysisContext context , Action < SyntaxTreeAnalysisContext > action )
15+ /// <summary>
16+ /// A cache of the result of computing whether a document has an auto-generated header.
17+ /// </summary>
18+ /// <remarks>
19+ /// This allows many analyzers that run on every token in the file to avoid checking
20+ /// the same state in the document repeatedly.
21+ /// </remarks>
22+ private static Tuple < WeakReference < Compilation > , ConcurrentDictionary < SyntaxTree , bool > > generatedHeaderCache
23+ = Tuple . Create ( new WeakReference < Compilation > ( null ) , default ( ConcurrentDictionary < SyntaxTree , bool > ) ) ;
24+
25+ /// <summary>
26+ /// Register an action to be executed at completion of parsing of a code document. A syntax tree action reports
27+ /// diagnostics about the <see cref="SyntaxTree"/> of a document.
28+ /// </summary>
29+ /// <remarks>This method honors exclusions.</remarks>
30+ /// <param name="context">The analysis context.</param>
31+ /// <param name="action">Action to be executed at completion of parsing of a document.</param>
32+ public static void RegisterSyntaxTreeActionHonorExclusions ( this CompilationStartAnalysisContext context , Action < SyntaxTreeAnalysisContext > action )
1433 {
34+ Compilation compilation = context . Compilation ;
35+ ConcurrentDictionary < SyntaxTree , bool > cache = GetOrCreateGeneratedDocumentCache ( compilation ) ;
36+
1537 context . RegisterSyntaxTreeAction (
1638 c =>
1739 {
18- if ( c . IsGeneratedDocument ( ) )
40+ if ( c . IsGeneratedDocument ( cache ) )
1941 {
2042 return ;
2143 }
@@ -28,12 +50,82 @@ internal static void RegisterSyntaxTreeActionHonorExclusions(this AnalysisContex
2850 } ) ;
2951 }
3052
31- internal static void RegisterSyntaxNodeActionHonorExclusions < TLanguageKindEnum > ( this AnalysisContext context , Action < SyntaxNodeAnalysisContext > action , params TLanguageKindEnum [ ] syntaxKinds ) where TLanguageKindEnum : struct
53+ /// <summary>
54+ /// Gets or creates a cache which can be used with <see cref="GeneratedCodeAnalysisExtensions"/> methods to
55+ /// efficiently determine whether or not a source file is considered generated.
56+ /// </summary>
57+ /// <param name="compilation">The compilation which the cache applies to.</param>
58+ /// <returns>A cache which tracks the syntax trees in a compilation which are considered generated.</returns>
59+ public static ConcurrentDictionary < SyntaxTree , bool > GetOrCreateGeneratedDocumentCache ( this Compilation compilation )
60+ {
61+ var headerCache = generatedHeaderCache ;
62+
63+ Compilation cachedCompilation ;
64+ if ( ! headerCache . Item1 . TryGetTarget ( out cachedCompilation ) || cachedCompilation != compilation )
65+ {
66+ var replacementCache = Tuple . Create ( new WeakReference < Compilation > ( compilation ) , new ConcurrentDictionary < SyntaxTree , bool > ( ) ) ;
67+ while ( true )
68+ {
69+ var prior = Interlocked . CompareExchange ( ref generatedHeaderCache , replacementCache , headerCache ) ;
70+ if ( prior == headerCache )
71+ {
72+ headerCache = replacementCache ;
73+ break ;
74+ }
75+
76+ headerCache = prior ;
77+ if ( headerCache . Item1 . TryGetTarget ( out cachedCompilation ) && cachedCompilation == compilation )
78+ {
79+ break ;
80+ }
81+ }
82+ }
83+
84+ return headerCache . Item2 ;
85+ }
86+
87+ /// <summary>
88+ /// Register an action to be executed at completion of semantic analysis of a <see cref="SyntaxNode"/> with an
89+ /// appropriate kind. A syntax node action can report diagnostics about a <see cref="SyntaxNode"/>, and can also
90+ /// collect state information to be used by other syntax node actions or code block end actions.
91+ /// </summary>
92+ /// <remarks>This method honors exclusions.</remarks>
93+ /// <param name="context">Action will be executed only if the kind of a <see cref="SyntaxNode"/> matches
94+ /// <paramref name="syntaxKind"/>.</param>
95+ /// <param name="action">Action to be executed at completion of semantic analysis of a
96+ /// <see cref="SyntaxNode"/>.</param>
97+ /// <param name="syntaxKind">The kind of syntax that should be analyzed.</param>
98+ /// <typeparam name="TLanguageKindEnum">Enum type giving the syntax node kinds of the source language for which
99+ /// the action applies.</typeparam>
100+ public static void RegisterSyntaxNodeActionHonorExclusions < TLanguageKindEnum > ( this CompilationStartAnalysisContext context , Action < SyntaxNodeAnalysisContext > action , TLanguageKindEnum syntaxKind )
101+ where TLanguageKindEnum : struct
102+ {
103+ context . RegisterSyntaxNodeActionHonorExclusions ( action , LanguageKindArrays < TLanguageKindEnum > . GetOrCreateArray ( syntaxKind ) ) ;
104+ }
105+
106+ /// <summary>
107+ /// Register an action to be executed at completion of semantic analysis of a <see cref="SyntaxNode"/> with an
108+ /// appropriate kind. A syntax node action can report diagnostics about a <see cref="SyntaxNode"/>, and can also
109+ /// collect state information to be used by other syntax node actions or code block end actions.
110+ /// </summary>
111+ /// <remarks>This method honors exclusions.</remarks>
112+ /// <param name="context">Action will be executed only if the kind of a <see cref="SyntaxNode"/> matches one of
113+ /// the <paramref name="syntaxKinds"/> values.</param>
114+ /// <param name="action">Action to be executed at completion of semantic analysis of a
115+ /// <see cref="SyntaxNode"/>.</param>
116+ /// <param name="syntaxKinds">The kinds of syntax that should be analyzed.</param>
117+ /// <typeparam name="TLanguageKindEnum">Enum type giving the syntax node kinds of the source language for which
118+ /// the action applies.</typeparam>
119+ public static void RegisterSyntaxNodeActionHonorExclusions < TLanguageKindEnum > ( this CompilationStartAnalysisContext context , Action < SyntaxNodeAnalysisContext > action , ImmutableArray < TLanguageKindEnum > syntaxKinds )
120+ where TLanguageKindEnum : struct
32121 {
122+ Compilation compilation = context . Compilation ;
123+ ConcurrentDictionary < SyntaxTree , bool > cache = GetOrCreateGeneratedDocumentCache ( compilation ) ;
124+
33125 context . RegisterSyntaxNodeAction (
34126 c =>
35127 {
36- if ( c . IsGenerated ( ) )
128+ if ( c . IsGenerated ( cache ) )
37129 {
38130 return ;
39131 }
@@ -46,5 +138,24 @@ internal static void RegisterSyntaxNodeActionHonorExclusions<TLanguageKindEnum>(
46138 } ,
47139 syntaxKinds ) ;
48140 }
141+
142+ private static class LanguageKindArrays < TLanguageKindEnum >
143+ where TLanguageKindEnum : struct
144+ {
145+ private static readonly ConcurrentDictionary < TLanguageKindEnum , ImmutableArray < TLanguageKindEnum > > Arrays =
146+ new ConcurrentDictionary < TLanguageKindEnum , ImmutableArray < TLanguageKindEnum > > ( ) ;
147+
148+ private static readonly Func < TLanguageKindEnum , ImmutableArray < TLanguageKindEnum > > CreateValueFactory = CreateValue ;
149+
150+ public static ImmutableArray < TLanguageKindEnum > GetOrCreateArray ( TLanguageKindEnum syntaxKind )
151+ {
152+ return Arrays . GetOrAdd ( syntaxKind , CreateValueFactory ) ;
153+ }
154+
155+ private static ImmutableArray < TLanguageKindEnum > CreateValue ( TLanguageKindEnum syntaxKind )
156+ {
157+ return ImmutableArray . Create ( syntaxKind ) ;
158+ }
159+ }
49160 }
50161}
0 commit comments