Skip to content

Commit a55758e

Browse files
committed
Add ServiceMethodReturnValueAnalyzer
Fixes #14
1 parent a8ab402 commit a55758e

3 files changed

Lines changed: 79 additions & 0 deletions

File tree

OpenStackNetAnalyzers/OpenStackNetAnalyzers/OpenStackNetAnalyzers.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
<Compile Include="Properties\AssemblyInfo.cs" />
5151
<Compile Include="SdkTypeSymbolExtensions.cs" />
5252
<Compile Include="ServiceMethodPrepareAsyncAnalyzer.cs" />
53+
<Compile Include="ServiceMethodReturnValueAnalyzer.cs" />
5354
<Compile Include="SpacingExtensions.cs" />
5455
<Compile Include="TypeSymbolExtensions.cs" />
5556
<Compile Include="XmlSyntaxFactory.cs" />
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
namespace OpenStackNetAnalyzers
2+
{
3+
using System.Collections.Immutable;
4+
using System.Linq;
5+
using Microsoft.CodeAnalysis;
6+
using Microsoft.CodeAnalysis.Diagnostics;
7+
8+
[DiagnosticAnalyzer(LanguageNames.CSharp)]
9+
public class ServiceMethodReturnValueAnalyzer : DiagnosticAnalyzer
10+
{
11+
public const string DiagnosticId = "ServiceMethodReturnValue";
12+
internal const string Title = "Service interface methods should return a Task<T> with a result that implements IHttpApiCall<T>";
13+
internal const string MessageFormat = "Service interface methods should return a Task<T> with a result that implements IHttpApiCall<T>";
14+
internal const string Category = "OpenStack.Maintainability";
15+
internal const string Description = "Service interface methods should return a Task<T> with a result that implements IHttpApiCall<T>";
16+
17+
private static DiagnosticDescriptor Descriptor =
18+
new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description);
19+
20+
private static readonly ImmutableArray<DiagnosticDescriptor> _supportedDiagnostics =
21+
ImmutableArray.Create(Descriptor);
22+
23+
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
24+
{
25+
get
26+
{
27+
return _supportedDiagnostics;
28+
}
29+
}
30+
31+
public override void Initialize(AnalysisContext context)
32+
{
33+
context.RegisterSymbolAction(HandleNamedType, SymbolKind.NamedType);
34+
}
35+
36+
private void HandleNamedType(SymbolAnalysisContext context)
37+
{
38+
INamedTypeSymbol symbol = (INamedTypeSymbol)context.Symbol;
39+
if (!symbol.IsHttpServiceInterface())
40+
return;
41+
42+
foreach (IMethodSymbol method in symbol.GetMembers().OfType<IMethodSymbol>())
43+
{
44+
INamedTypeSymbol returnType = method.ReturnType as INamedTypeSymbol;
45+
if (returnType.IsTask() && returnType.IsGenericType && returnType.TypeArguments.Length == 1)
46+
{
47+
INamedTypeSymbol genericArgument = returnType.TypeArguments[0] as INamedTypeSymbol;
48+
if (genericArgument.IsDelegatingHttpApiCall())
49+
{
50+
// the method returns the expected type
51+
continue;
52+
}
53+
}
54+
55+
ImmutableArray<Location> locations = method.Locations;
56+
context.ReportDiagnostic(Diagnostic.Create(Descriptor, locations.FirstOrDefault(), locations.Skip(1)));
57+
}
58+
}
59+
}
60+
}

OpenStackNetAnalyzers/OpenStackNetAnalyzers/TypeSymbolExtensions.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ internal static class TypeSymbolExtensions
77
{
88
private const string FullyQualifiedImmutableArrayT = "global::System.Collections.Immutable.ImmutableArray<T>";
99

10+
private const string FullyQualifiedTask = "global::System.Threading.Tasks.Task";
11+
1012
public static bool IsNonNullableValueType(this ITypeSymbol type)
1113
{
1214
if (type == null)
@@ -47,5 +49,21 @@ public static bool IsImmutableArray(this ITypeSymbol type)
4749

4850
return string.Equals(FullyQualifiedImmutableArrayT, originalDefinition.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), StringComparison.Ordinal);
4951
}
52+
53+
public static bool IsTask(this INamedTypeSymbol symbol)
54+
{
55+
while (symbol != null && symbol.SpecialType != SpecialType.System_Object)
56+
{
57+
if (!symbol.IsGenericType)
58+
{
59+
if (string.Equals(FullyQualifiedTask, symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), StringComparison.Ordinal))
60+
return true;
61+
}
62+
63+
symbol = symbol.BaseType;
64+
}
65+
66+
return false;
67+
}
5068
}
5169
}

0 commit comments

Comments
 (0)