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
7
2
{
8
3
using System ;
4
+ using System . Collections . Concurrent ;
5
+ using System . Collections . Immutable ;
6
+ using System . Threading ;
7
+ using Microsoft . CodeAnalysis ;
9
8
using Microsoft . CodeAnalysis . Diagnostics ;
10
9
10
+ /// <summary>
11
+ /// Provides extension methods to deal for analyzers.
12
+ /// </summary>
11
13
internal static class AnalyzerExtensions
12
14
{
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 )
14
33
{
34
+ Compilation compilation = context . Compilation ;
35
+ ConcurrentDictionary < SyntaxTree , bool > cache = GetOrCreateGeneratedDocumentCache ( compilation ) ;
36
+
15
37
context . RegisterSyntaxTreeAction (
16
38
c =>
17
39
{
18
- if ( c . IsGeneratedDocument ( ) )
40
+ if ( c . IsGeneratedDocument ( cache ) )
19
41
{
20
42
return ;
21
43
}
@@ -28,12 +50,82 @@ internal static void RegisterSyntaxTreeActionHonorExclusions(this AnalysisContex
28
50
} ) ;
29
51
}
30
52
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
32
121
{
122
+ Compilation compilation = context . Compilation ;
123
+ ConcurrentDictionary < SyntaxTree , bool > cache = GetOrCreateGeneratedDocumentCache ( compilation ) ;
124
+
33
125
context . RegisterSyntaxNodeAction (
34
126
c =>
35
127
{
36
- if ( c . IsGenerated ( ) )
128
+ if ( c . IsGenerated ( cache ) )
37
129
{
38
130
return ;
39
131
}
@@ -46,5 +138,24 @@ internal static void RegisterSyntaxNodeActionHonorExclusions<TLanguageKindEnum>(
46
138
} ,
47
139
syntaxKinds ) ;
48
140
}
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
+ }
49
160
}
50
161
}
0 commit comments