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

Commit da32596

Browse files
committed
Extract extension methods for symbol analysis
1 parent 5d31162 commit da32596

File tree

4 files changed

+131
-70
lines changed

4 files changed

+131
-70
lines changed

AsyncUsageAnalyzers/AsyncUsageAnalyzers/AsyncUsageAnalyzers.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,9 @@
5555
<DesignTime>True</DesignTime>
5656
<DependentUpon>HelpersResources.resx</DependentUpon>
5757
</Compile>
58+
<Compile Include="Helpers\MethodSymbolExtensions.cs" />
5859
<Compile Include="Helpers\SpecializedTasks.cs" />
60+
<Compile Include="Helpers\SymbolExtensions.cs" />
5961
<Compile Include="Helpers\TriviaHelper.cs" />
6062
<Compile Include="Naming\AvoidAsyncSuffixAnalyzer.cs" />
6163
<Compile Include="Naming\NamingResources.Designer.cs">
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
2+
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
3+
4+
namespace AsyncUsageAnalyzers.Helpers
5+
{
6+
using System;
7+
using System.Threading.Tasks;
8+
using Microsoft.CodeAnalysis;
9+
10+
internal static class MethodSymbolExtensions
11+
{
12+
public static bool HasAsyncSignature(this IMethodSymbol symbol, bool treatAsyncVoidAsAsync = false)
13+
{
14+
// void-returning methods are not asynchronous according to their signature, even if they use `async`
15+
if (symbol.ReturnsVoid)
16+
{
17+
if (treatAsyncVoidAsAsync)
18+
{
19+
return symbol.IsAsync;
20+
}
21+
22+
return false;
23+
}
24+
25+
if (!symbol.IsAsync)
26+
{
27+
// This check conveniently covers Task and Task<T> by ignoring the `1 in Task<T>.
28+
if (!string.Equals(nameof(Task), symbol.ReturnType?.Name, StringComparison.Ordinal))
29+
{
30+
return false;
31+
}
32+
33+
if (!string.Equals(typeof(Task).Namespace, symbol.ReturnType?.ContainingNamespace?.ToString(), StringComparison.Ordinal))
34+
{
35+
return false;
36+
}
37+
}
38+
39+
return true;
40+
}
41+
42+
public static bool IsTestMethod(this IMethodSymbol methodSymbol)
43+
{
44+
foreach (AttributeData attributeData in methodSymbol.GetAttributes())
45+
{
46+
var attributeClass = attributeData.AttributeClass;
47+
if (attributeClass == null)
48+
{
49+
continue;
50+
}
51+
52+
if (string.Equals(attributeClass.Name, "TestMethodAttribute", StringComparison.Ordinal)
53+
|| string.Equals(attributeClass.Name, "FactAttribute", StringComparison.Ordinal)
54+
|| string.Equals(attributeClass.Name, "TheoryAttribute", StringComparison.Ordinal)
55+
|| string.Equals(attributeClass.Name, "TestAttribute", StringComparison.Ordinal))
56+
{
57+
return true;
58+
}
59+
}
60+
61+
return false;
62+
}
63+
64+
public static bool IsOverrideOrImplementation(this IMethodSymbol symbol)
65+
{
66+
if (symbol.IsOverride)
67+
{
68+
return true;
69+
}
70+
71+
if (!symbol.ExplicitInterfaceImplementations.IsDefaultOrEmpty)
72+
{
73+
return true;
74+
}
75+
76+
if ((symbol.ContainingType.TypeKind == TypeKind.Class || symbol.ContainingType.TypeKind == TypeKind.Struct)
77+
&& symbol.DeclaredAccessibility == Accessibility.Public)
78+
{
79+
// As a final check, make sure the method isn't implicitly implementing an interface method
80+
foreach (INamedTypeSymbol interfaceType in symbol.ContainingType.AllInterfaces)
81+
{
82+
foreach (var member in interfaceType.GetMembers(symbol.Name))
83+
{
84+
if (Equals(symbol.ContainingType.FindImplementationForInterfaceMember(member), symbol))
85+
{
86+
return true;
87+
}
88+
}
89+
}
90+
}
91+
92+
return false;
93+
}
94+
}
95+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
2+
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
3+
4+
namespace AsyncUsageAnalyzers.Helpers
5+
{
6+
using System.Collections.Concurrent;
7+
using System.Threading;
8+
using Microsoft.CodeAnalysis;
9+
10+
internal static class SymbolExtensions
11+
{
12+
public static bool IsInAnalyzedSource(this ISymbol symbol, ConcurrentDictionary<SyntaxTree, bool> generatedHeaderCache, CancellationToken cancellationToken)
13+
{
14+
if (symbol.Locations.IsDefaultOrEmpty)
15+
{
16+
return false;
17+
}
18+
19+
Location location = symbol.Locations[0];
20+
if (!location.IsInSource || location.SourceTree.IsGeneratedDocument(generatedHeaderCache, cancellationToken))
21+
{
22+
return false;
23+
}
24+
25+
return true;
26+
}
27+
}
28+
}

AsyncUsageAnalyzers/AsyncUsageAnalyzers/Usage/IncludeCancellationParameterAnalyzer.cs

Lines changed: 6 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ namespace AsyncUsageAnalyzers.Usage
77
using System.Collections.Concurrent;
88
using System.Collections.Immutable;
99
using System.Threading;
10-
using System.Threading.Tasks;
10+
using Helpers;
1111
using Microsoft.CodeAnalysis;
1212
using Microsoft.CodeAnalysis.Diagnostics;
1313

@@ -73,53 +73,27 @@ public void HandleMethodDeclaration(SymbolAnalysisContext context)
7373
return;
7474
}
7575

76-
if (symbol.Locations.IsDefaultOrEmpty)
76+
if (symbol.IsImplicitlyDeclared)
7777
{
7878
return;
7979
}
8080

81-
if (symbol.IsOverride)
81+
if (!symbol.IsInAnalyzedSource(this.generatedHeaderCache, context.CancellationToken))
8282
{
8383
return;
8484
}
8585

86-
if (!symbol.ExplicitInterfaceImplementations.IsDefaultOrEmpty)
86+
if (!symbol.HasAsyncSignature())
8787
{
8888
return;
8989
}
9090

91-
Location location = symbol.Locations[0];
92-
if (!location.IsInSource || location.SourceTree.IsGeneratedDocument(this.generatedHeaderCache, context.CancellationToken))
91+
if (symbol.IsOverrideOrImplementation())
9392
{
9493
return;
9594
}
9695

97-
// void-returning methods are not asynchronous according to their signature, even if they use `async`
98-
if (symbol.ReturnsVoid)
99-
{
100-
return;
101-
}
102-
103-
if (!symbol.IsAsync)
104-
{
105-
// This check conveniently covers Task and Task<T> by ignoring the `1 in Task<T>.
106-
if (!string.Equals(nameof(Task), symbol.ReturnType?.Name, StringComparison.Ordinal))
107-
{
108-
return;
109-
}
110-
111-
if (!string.Equals(typeof(Task).Namespace, symbol.ReturnType?.ContainingNamespace?.ToString(), StringComparison.Ordinal))
112-
{
113-
return;
114-
}
115-
}
116-
117-
if (symbol.MethodKind == MethodKind.PropertyGet || symbol.MethodKind == MethodKind.PropertySet)
118-
{
119-
return;
120-
}
121-
122-
if (IsTestMethod(symbol))
96+
if (symbol.IsTestMethod())
12397
{
12498
return;
12599
}
@@ -133,22 +107,6 @@ public void HandleMethodDeclaration(SymbolAnalysisContext context)
133107
}
134108
}
135109

136-
if ((symbol.ContainingType.TypeKind == TypeKind.Class || symbol.ContainingType.TypeKind == TypeKind.Struct)
137-
&& symbol.DeclaredAccessibility == Accessibility.Public)
138-
{
139-
// As a final check, make sure the method isn't implicitly implementing an interface method
140-
foreach (INamedTypeSymbol interfaceType in symbol.ContainingType.AllInterfaces)
141-
{
142-
foreach (var member in interfaceType.GetMembers(symbol.Name))
143-
{
144-
if (Equals(symbol.ContainingType.FindImplementationForInterfaceMember(member), symbol))
145-
{
146-
return;
147-
}
148-
}
149-
}
150-
}
151-
152110
foreach (var parameterSymbol in symbol.Parameters)
153111
{
154112
if (parameterSymbol.RefKind == RefKind.Out)
@@ -178,28 +136,6 @@ public void HandleMethodDeclaration(SymbolAnalysisContext context)
178136

179137
context.ReportDiagnostic(Diagnostic.Create(Descriptor, symbol.Locations[0], symbol.Name));
180138
}
181-
182-
private static bool IsTestMethod(IMethodSymbol methodSymbol)
183-
{
184-
foreach (AttributeData attributeData in methodSymbol.GetAttributes())
185-
{
186-
var attributeClass = attributeData.AttributeClass;
187-
if (attributeClass == null)
188-
{
189-
continue;
190-
}
191-
192-
if (string.Equals(attributeClass.Name, "TestMethodAttribute", StringComparison.Ordinal)
193-
|| string.Equals(attributeClass.Name, "FactAttribute", StringComparison.Ordinal)
194-
|| string.Equals(attributeClass.Name, "TheoryAttribute", StringComparison.Ordinal)
195-
|| string.Equals(attributeClass.Name, "TestAttribute", StringComparison.Ordinal))
196-
{
197-
return true;
198-
}
199-
}
200-
201-
return false;
202-
}
203139
}
204140
}
205141
}

0 commit comments

Comments
 (0)