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

Commit d289209

Browse files
committed
Add extensions for handling generated code
1 parent fee1652 commit d289209

File tree

3 files changed

+239
-0
lines changed

3 files changed

+239
-0
lines changed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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
7+
{
8+
using System;
9+
using Microsoft.CodeAnalysis.Diagnostics;
10+
11+
internal static class AnalyzerExtensions
12+
{
13+
internal static void RegisterSyntaxTreeActionHonorExclusions(this AnalysisContext context, Action<SyntaxTreeAnalysisContext> action)
14+
{
15+
context.RegisterSyntaxTreeAction(
16+
c =>
17+
{
18+
if (c.IsGeneratedDocument())
19+
{
20+
return;
21+
}
22+
23+
// Honor the containing document item's ExcludeFromStylecop=True
24+
// MSBuild metadata, if analyzers have access to it.
25+
//// TODO: code here
26+
27+
action(c);
28+
});
29+
}
30+
31+
internal static void RegisterSyntaxNodeActionHonorExclusions<TLanguageKindEnum>(this AnalysisContext context, Action<SyntaxNodeAnalysisContext> action, params TLanguageKindEnum[] syntaxKinds) where TLanguageKindEnum : struct
32+
{
33+
context.RegisterSyntaxNodeAction(
34+
c =>
35+
{
36+
if (c.IsGenerated())
37+
{
38+
return;
39+
}
40+
41+
// Honor the containing document item's ExcludeFromStylecop=True
42+
// MSBuild metadata, if analyzers have access to it.
43+
//// TODO: code here
44+
45+
action(c);
46+
},
47+
syntaxKinds);
48+
}
49+
}
50+
}

AsyncUsageAnalyzers/AsyncUsageAnalyzers/AsyncUsageAnalyzers.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@
4343
</PropertyGroup>
4444
<ItemGroup>
4545
<Compile Include="AnalyzerConstants.cs" />
46+
<Compile Include="AnalyzerExtensions.cs" />
47+
<Compile Include="GeneratedCodeAnalysisExtensions.cs" />
4648
<Compile Include="Properties\AssemblyInfo.cs" />
4749
<Compile Include="Resources.Designer.cs">
4850
<AutoGen>True</AutoGen>
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
// This file originally obtained from
2+
// https://raw.githubusercontent.com/code-cracker/code-cracker/08c1a01337964924eeed12be8b14c8ce8ec6b626/src/Common/CodeCracker.Common/Extensions/GeneratedCodeAnalysisExtensions.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
7+
{
8+
using System.IO;
9+
using System.Linq;
10+
using System.Runtime.CompilerServices;
11+
using System.Text.RegularExpressions;
12+
using System.Threading;
13+
using Microsoft.CodeAnalysis;
14+
using Microsoft.CodeAnalysis.CSharp;
15+
using Microsoft.CodeAnalysis.CSharp.Syntax;
16+
using Microsoft.CodeAnalysis.Diagnostics;
17+
18+
internal static class GeneratedCodeAnalysisExtensions
19+
{
20+
/// <summary>
21+
/// A cache of the result of computing whether a document has an auto-generated header.
22+
/// </summary>
23+
/// <remarks>
24+
/// This allows many analyzers that run on every token in the file to avoid checking
25+
/// the same state in the document repeatedly.
26+
/// </remarks>
27+
private static readonly ConditionalWeakTable<SyntaxTree, StrongBox<bool?>> GeneratedHeaderPresentCheck
28+
= new ConditionalWeakTable<SyntaxTree, StrongBox<bool?>>();
29+
30+
/// <summary>
31+
/// Checks whether the given node or its containing document is auto generated by a tool.
32+
/// </summary>
33+
/// <remarks>
34+
/// <para>This method uses <see cref="IsGeneratedDocument(SyntaxTree, CancellationToken)"/> to determine which
35+
/// code is considered "generated".</para>
36+
/// </remarks>
37+
/// <param name="context">The analysis context for a <see cref="SyntaxNode"/>.</param>
38+
/// <returns>
39+
/// <para><see langword="true"/> if the <see cref="SyntaxNode"/> contained in <paramref name="context"/> is
40+
/// located in generated code; otherwise, <see langword="false"/>.</para>
41+
/// </returns>
42+
internal static bool IsGenerated(this SyntaxNodeAnalysisContext context)
43+
{
44+
return IsGeneratedDocument(context.Node.SyntaxTree, context.CancellationToken);
45+
}
46+
47+
/// <summary>
48+
/// Checks whether the given document is auto generated by a tool.
49+
/// </summary>
50+
/// <remarks>
51+
/// <para>This method uses <see cref="IsGeneratedDocument(SyntaxTree, CancellationToken)"/> to determine which
52+
/// code is considered "generated".</para>
53+
/// </remarks>
54+
/// <param name="context">The analysis context for a <see cref="SyntaxTree"/>.</param>
55+
/// <returns>
56+
/// <para><see langword="true"/> if the <see cref="SyntaxTree"/> contained in <paramref name="context"/> is
57+
/// located in generated code; otherwise, <see langword="false"/>.</para>
58+
/// </returns>
59+
internal static bool IsGeneratedDocument(this SyntaxTreeAnalysisContext context)
60+
{
61+
return IsGeneratedDocument(context.Tree, context.CancellationToken);
62+
}
63+
64+
/// <summary>
65+
/// Checks whether the given document is auto generated by a tool
66+
/// (based on filename or comment header).
67+
/// </summary>
68+
/// <remarks>
69+
/// <para>The exact conditions used to identify generated code are subject to change in future releases. The current algorithm uses the following checks.</para>
70+
/// <para>Code is considered generated if it meets any of the following conditions.</para>
71+
/// <list type="bullet">
72+
/// <item>The code is contained in a file which starts with a comment containing the text
73+
/// <c>&lt;auto-generated</c>.</item>
74+
/// <item>The code is contained in a file with a name matching certain patterns (case-insensitive):
75+
/// <list type="bullet">
76+
/// <item>service.cs</item>
77+
/// <item>TemporaryGeneratedFile_*.cs</item>
78+
/// <item>assemblyinfo.cs</item>
79+
/// <item>assemblyattributes.cs</item>
80+
/// <item>*.g.cs</item>
81+
/// <item>*.g.i.cs</item>
82+
/// <item>*.designer.cs</item>
83+
/// <item>*.generated.cs</item>
84+
/// <item>*.assemblyattributes.cs</item>
85+
/// </list>
86+
/// </item>
87+
/// </list>
88+
/// </remarks>
89+
/// <param name="tree">The syntax tree to examine.</param>
90+
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that the task will observe.</param>
91+
/// <returns>
92+
/// <para><see langword="true"/> if <paramref name="tree"/> is located in generated code; otherwise,
93+
/// <see langword="false"/>. If <paramref name="tree"/> is <see langword="null"/>, this method returns
94+
/// <see langword="false"/>.</para>
95+
/// </returns>
96+
public static bool IsGeneratedDocument(this SyntaxTree tree, CancellationToken cancellationToken)
97+
{
98+
if (tree == null)
99+
{
100+
return false;
101+
}
102+
103+
StrongBox<bool?> cachedResult = GeneratedHeaderPresentCheck.GetOrCreateValue(tree);
104+
if (cachedResult.Value.HasValue)
105+
{
106+
return cachedResult.Value.Value;
107+
}
108+
109+
bool autoGenerated = IsGeneratedDocumentNoCache(tree, cancellationToken);
110+
111+
// Update the strongbox's value with our computed result.
112+
// This doesn't change the strongbox reference, and its presence in the
113+
// ConditionalWeakTable is already assured, so we're updating in-place.
114+
// In the event of a race condition with another thread that set the value,
115+
// we'll just be re-setting it to the same value.
116+
cachedResult.Value = autoGenerated;
117+
118+
return autoGenerated;
119+
}
120+
121+
private static bool IsGeneratedDocumentNoCache(SyntaxTree tree, CancellationToken cancellationToken)
122+
{
123+
return IsGeneratedFileName(tree.FilePath) || HasAutoGeneratedComment(tree, cancellationToken);
124+
}
125+
126+
/// <summary>
127+
/// Checks whether the given document has an auto-generated comment as its header.
128+
/// </summary>
129+
/// <param name="tree">The syntax tree to examine.</param>
130+
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that the task will observe.</param>
131+
/// <returns>
132+
/// <para><see langword="true"/> if <paramref name="tree"/> starts with a comment containing the text
133+
/// <c>&lt;auto-generated</c>; otherwise, <see langword="false"/>.</para>
134+
/// </returns>
135+
private static bool HasAutoGeneratedComment(SyntaxTree tree, CancellationToken cancellationToken)
136+
{
137+
var root = tree.GetRoot(cancellationToken);
138+
139+
if (root == null)
140+
{
141+
return false;
142+
}
143+
144+
var firstToken = root.GetFirstToken();
145+
SyntaxTriviaList trivia;
146+
if (firstToken == default(SyntaxToken))
147+
{
148+
var token = ((CompilationUnitSyntax)root).EndOfFileToken;
149+
if (!token.HasLeadingTrivia)
150+
{
151+
return false;
152+
}
153+
154+
trivia = token.LeadingTrivia;
155+
}
156+
else
157+
{
158+
if (!firstToken.HasLeadingTrivia)
159+
{
160+
return false;
161+
}
162+
163+
trivia = firstToken.LeadingTrivia;
164+
}
165+
166+
var comments = trivia.Where(t => t.IsKind(SyntaxKind.SingleLineCommentTrivia) || t.IsKind(SyntaxKind.MultiLineCommentTrivia));
167+
return comments.Any(t => t.ToString().Contains("<auto-generated"));
168+
}
169+
170+
/// <summary>
171+
/// Checks whether the given document has a filename that indicates it is a generated file.
172+
/// </summary>
173+
/// <param name="filePath">The source file name, without any path.</param>
174+
/// <returns>
175+
/// <para><see langword="true"/> if <paramref name="filePath"/> is the name of a generated file; otherwise,
176+
/// <see langword="false"/>.</para>
177+
/// </returns>
178+
/// <seealso cref="IsGeneratedDocument(SyntaxTree, CancellationToken)"/>
179+
private static bool IsGeneratedFileName(string filePath)
180+
{
181+
return Regex.IsMatch(
182+
Path.GetFileName(filePath),
183+
@"(^service|^TemporaryGeneratedFile_.*|^assemblyinfo|^assemblyattributes|\.(g\.i|g|designer|generated|assemblyattributes))\.(cs|vb)$",
184+
RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture);
185+
}
186+
}
187+
}

0 commit comments

Comments
 (0)