Skip to content
This repository was archived by the owner on Nov 8, 2018. It is now read-only.

Commit 6c21337

Browse files
committed
Update to latest analyzer patterns from StyleCop.Analyzers
* Improve generated code detection * Use static methods where possible * Reduce allocations * All types are internal
1 parent a0fba43 commit 6c21337

14 files changed

+919
-181
lines changed

AsyncUsageAnalyzers/AsyncUsageAnalyzers.CodeFixes/Naming/AvoidAsyncSuffixCodeFixProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
/// </summary>
1414
[ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic, Name = nameof(AvoidAsyncSuffixCodeFixProvider))]
1515
[Shared]
16-
public class AvoidAsyncSuffixCodeFixProvider : CodeFixProvider
16+
internal class AvoidAsyncSuffixCodeFixProvider : CodeFixProvider
1717
{
1818
private static readonly ImmutableArray<string> FixableDiagnostics =
1919
ImmutableArray.Create(AvoidAsyncSuffixAnalyzer.DiagnosticId);

AsyncUsageAnalyzers/AsyncUsageAnalyzers.CodeFixes/Naming/UseAsyncSuffixCodeFixProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
/// </summary>
1414
[ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic, Name = nameof(UseAsyncSuffixCodeFixProvider))]
1515
[Shared]
16-
public class UseAsyncSuffixCodeFixProvider : CodeFixProvider
16+
internal class UseAsyncSuffixCodeFixProvider : CodeFixProvider
1717
{
1818
private static readonly ImmutableArray<string> FixableDiagnostics =
1919
ImmutableArray.Create(UseAsyncSuffixAnalyzer.DiagnosticId);

AsyncUsageAnalyzers/AsyncUsageAnalyzers.CodeFixes/Usage/UseConfigureAwaitCodeFixProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
/// </summary>
1717
[ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic, Name = nameof(UseConfigureAwaitCodeFixProvider))]
1818
[Shared]
19-
public class UseConfigureAwaitCodeFixProvider : CodeFixProvider
19+
internal class UseConfigureAwaitCodeFixProvider : CodeFixProvider
2020
{
2121
private static readonly ImmutableArray<string> FixableDiagnostics =
2222
ImmutableArray.Create(UseConfigureAwaitAnalyzer.DiagnosticId);

AsyncUsageAnalyzers/AsyncUsageAnalyzers/AnalyzerConstants.cs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
namespace AsyncUsageAnalyzers
22
{
3+
using System.Diagnostics.CodeAnalysis;
34
using Microsoft.CodeAnalysis;
45

56
internal static class AnalyzerConstants
@@ -24,6 +25,49 @@ static AnalyzerConstants()
2425
/// <see cref="DiagnosticDescriptor(string, string, string, string, DiagnosticSeverity, bool, string, string, string[])"/>
2526
/// to disable a diagnostic which is currently untested.
2627
/// </value>
28+
[ExcludeFromCodeCoverage]
29+
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1623:Property summary documentation must match accessors.", Justification = "This property behaves more like an opaque value than a Boolean.")]
2730
internal static bool DisabledNoTests { get; }
31+
32+
/// <summary>
33+
/// Gets a reference value which can be passed to
34+
/// <see cref="DiagnosticDescriptor(string, string, string, string, DiagnosticSeverity, bool, string, string, string[])"/>
35+
/// to indicate that the diagnostic is disabled by default because it is an alternative to a reference StyleCop
36+
/// rule.
37+
/// </summary>
38+
/// <value>
39+
/// A reference value which can be passed to
40+
/// <see cref="DiagnosticDescriptor(string, string, string, string, DiagnosticSeverity, bool, string, string, string[])"/>
41+
/// to indicate that the diagnostic is disabled by default because it is an alternative to a reference StyleCop
42+
/// rule.
43+
/// </value>
44+
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1623:Property summary documentation must match accessors.", Justification = "This property behaves more like an opaque value than a Boolean.")]
45+
internal static bool DisabledAlternative => false;
46+
47+
/// <summary>
48+
/// Gets a reference value which can be passed to
49+
/// <see cref="DiagnosticDescriptor(string, string, string, string, DiagnosticSeverity, bool, string, string, string[])"/>
50+
/// to indicate that the diagnostic should be enabled by default.
51+
/// </summary>
52+
/// <value>
53+
/// A reference value which can be passed to
54+
/// <see cref="DiagnosticDescriptor(string, string, string, string, DiagnosticSeverity, bool, string, string, string[])"/>
55+
/// to indicate that the diagnostic should be enabled by default.
56+
/// </value>
57+
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1623:Property summary documentation must match accessors.", Justification = "This property behaves more like an opaque value than a Boolean.")]
58+
internal static bool EnabledByDefault => true;
59+
60+
/// <summary>
61+
/// Gets a reference value which can be passed to
62+
/// <see cref="DiagnosticDescriptor(string, string, string, string, DiagnosticSeverity, bool, string, string, string[])"/>
63+
/// to indicate that the diagnostic should be disabled by default.
64+
/// </summary>
65+
/// <value>
66+
/// A reference value which can be passed to
67+
/// <see cref="DiagnosticDescriptor(string, string, string, string, DiagnosticSeverity, bool, string, string, string[])"/>
68+
/// to indicate that the diagnostic should be disabled by default.
69+
/// </value>
70+
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1623:Property summary documentation must match accessors.", Justification = "This property behaves more like an opaque value than a Boolean.")]
71+
internal static bool DisabledByDefault => false;
2872
}
2973
}
Lines changed: 121 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,43 @@
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
}

AsyncUsageAnalyzers/AsyncUsageAnalyzers/AsyncUsageAnalyzers.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@
4646
<ItemGroup>
4747
<Compile Include="AnalyzerConstants.cs" />
4848
<Compile Include="AnalyzerExtensions.cs" />
49+
<Compile Include="ExcludeFromCodeCoverageAttribute.cs" />
4950
<Compile Include="GeneratedCodeAnalysisExtensions.cs" />
51+
<Compile Include="Helpers\TriviaHelper.cs" />
5052
<Compile Include="Naming\AvoidAsyncSuffixAnalyzer.cs" />
5153
<Compile Include="Naming\NamingResources.Designer.cs">
5254
<AutoGen>True</AutoGen>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace System.Diagnostics.CodeAnalysis
2+
{
3+
[AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = false)]
4+
internal sealed class ExcludeFromCodeCoverageAttribute : Attribute
5+
{
6+
}
7+
}

0 commit comments

Comments
 (0)