Skip to content

Commit f04a881

Browse files
committed
Merge pull request #1671 from sharwell/weakreference
2 parents a6f0ea7 + 919d61a commit f04a881

8 files changed

Lines changed: 533 additions & 429 deletions

File tree

StyleCop.Analyzers/StyleCop.Analyzers/AnalyzerExtensions.cs

Lines changed: 38 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -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>&lt;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

Comments
 (0)