Skip to content

Commit

Permalink
Merge pull request #1671 from sharwell/weakreference
Browse files Browse the repository at this point in the history
  • Loading branch information
sharwell committed Oct 23, 2015
2 parents a6f0ea7 + 919d61a commit f04a881
Show file tree
Hide file tree
Showing 8 changed files with 533 additions and 429 deletions.
74 changes: 38 additions & 36 deletions StyleCop.Analyzers/StyleCop.Analyzers/AnalyzerExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ namespace StyleCop.Analyzers
{
using System;
using System.Collections.Concurrent;
using System.Runtime.CompilerServices;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
Expand All @@ -22,8 +21,8 @@ internal static class AnalyzerExtensions
/// This allows many analyzers that run on every token in the file to avoid checking
/// the same state in the document repeatedly.
/// </remarks>
private static readonly ConditionalWeakTable<Compilation, ConcurrentDictionary<SyntaxTree, bool>> GeneratedHeaderCache
= new ConditionalWeakTable<Compilation, ConcurrentDictionary<SyntaxTree, bool>>();
private static Tuple<WeakReference<Compilation>, ConcurrentDictionary<SyntaxTree, bool>> generatedHeaderCache
= Tuple.Create(new WeakReference<Compilation>(null), default(ConcurrentDictionary<SyntaxTree, bool>));

/// <summary>
/// Register an action to be executed at completion of parsing of a code document. A syntax tree action reports
Expand All @@ -35,7 +34,7 @@ private static readonly ConditionalWeakTable<Compilation, ConcurrentDictionary<S
public static void RegisterSyntaxTreeActionHonorExclusions(this CompilationStartAnalysisContext context, Action<SyntaxTreeAnalysisContext> action)
{
Compilation compilation = context.Compilation;
ConcurrentDictionary<SyntaxTree, bool> cache = GeneratedHeaderCache.GetOrCreateValue(compilation);
ConcurrentDictionary<SyntaxTree, bool> cache = GetOrCreateGeneratedDocumentCache(compilation);

context.RegisterSyntaxTreeAction(
c =>
Expand All @@ -53,6 +52,40 @@ public static void RegisterSyntaxTreeActionHonorExclusions(this CompilationStart
});
}

/// <summary>
/// Gets or creates a cache which can be used with <see cref="GeneratedCodeAnalysisExtensions"/> methods to
/// efficiently determine whether or not a source file is considered generated.
/// </summary>
/// <param name="compilation">The compilation which the cache applies to.</param>
/// <returns>A cache which tracks the syntax trees in a compilation which are considered generated.</returns>
public static ConcurrentDictionary<SyntaxTree, bool> GetOrCreateGeneratedDocumentCache(this Compilation compilation)
{
var headerCache = generatedHeaderCache;

Compilation cachedCompilation;
if (!headerCache.Item1.TryGetTarget(out cachedCompilation) || cachedCompilation != compilation)
{
var replacementCache = Tuple.Create(new WeakReference<Compilation>(compilation), new ConcurrentDictionary<SyntaxTree, bool>());
while (true)
{
var prior = Interlocked.CompareExchange(ref generatedHeaderCache, replacementCache, headerCache);
if (prior == headerCache)
{
headerCache = replacementCache;
break;
}

headerCache = prior;
if (headerCache.Item1.TryGetTarget(out cachedCompilation) && cachedCompilation == compilation)
{
break;
}
}
}

return headerCache.Item2;
}

/// <summary>
/// Register an action to be executed at completion of semantic analysis of a <see cref="SyntaxNode"/> with an
/// appropriate kind. A syntax node action can report diagnostics about a <see cref="SyntaxNode"/>, and can also
Expand All @@ -70,7 +103,7 @@ public static void RegisterSyntaxNodeActionHonorExclusions<TLanguageKindEnum>(th
where TLanguageKindEnum : struct
{
Compilation compilation = context.Compilation;
ConcurrentDictionary<SyntaxTree, bool> cache = GeneratedHeaderCache.GetOrCreateValue(compilation);
ConcurrentDictionary<SyntaxTree, bool> cache = GetOrCreateGeneratedDocumentCache(compilation);

context.RegisterSyntaxNodeAction(
c =>
Expand All @@ -88,36 +121,5 @@ public static void RegisterSyntaxNodeActionHonorExclusions<TLanguageKindEnum>(th
},
syntaxKinds);
}

/// <summary>
/// Checks whether the given document is auto generated by a tool (based on filename or comment header).
/// </summary>
/// <remarks>
/// <para>The exact conditions used to identify generated code are subject to change in future releases. The
/// current algorithm uses the following checks.</para>
/// <para>Code is considered generated if it meets any of the following conditions.</para>
/// <list type="bullet">
/// <item>The code is contained in a file which starts with a comment containing the text
/// <c>&lt;auto-generated</c>.</item>
/// <item>The code is contained in a file with a name matching certain patterns (case-insensitive):
/// <list type="bullet">
/// <item>*.designer.cs</item>
/// </list>
/// </item>
/// </list>
/// </remarks>
/// <param name="tree">The syntax tree to examine.</param>
/// <param name="compilation">The <see cref="Compilation"/> containing the specified <paramref name="tree"/>.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that the task will observe.</param>
/// <returns>
/// <para><see langword="true"/> if <paramref name="tree"/> is located in generated code; otherwise,
/// <see langword="false"/>. If <paramref name="tree"/> is <see langword="null"/>, this method returns
/// <see langword="false"/>.</para>
/// </returns>
public static bool IsGeneratedDocument(this SyntaxTree tree, Compilation compilation, CancellationToken cancellationToken)
{
ConcurrentDictionary<SyntaxTree, bool> cache = GeneratedHeaderCache.GetOrCreateValue(compilation);
return tree.IsGeneratedDocument(cache, cancellationToken);
}
}
}
Loading

0 comments on commit f04a881

Please sign in to comment.