Skip to content

Commit bcf2095

Browse files
authored
Merge pull request #8 from PandaTechAM/development
Final cleanup and minor fixes
2 parents 09e2086 + 03bed65 commit bcf2095

13 files changed

+435
-533
lines changed

src/Analyzers/AnalyzerReleases.Shipped.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
## Release 1.0.0
1+
## Release 1.6.0
22

33
### New Rules
44

src/Analyzers/Analyzers.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@
1818
<PackageId>Pandatech.Analyzers</PackageId>
1919
<PackageIcon>pandatech.png</PackageIcon>
2020
<PackageReadmeFile>Readme.md</PackageReadmeFile>
21-
<Version>1.5.0</Version>
21+
<Version>1.6.0</Version>
2222
<Authors>Pandatech</Authors>
2323
<Description>Pandatech Roslyn analyzers enforcing company coding rules.</Description>
2424
<PackageLicenseExpression>MIT</PackageLicenseExpression>
2525
<PackageTags>Pandatech, analyzers, roslyn, async, cancellation, coding-rules</PackageTags>
2626
<RepositoryUrl>https://github.com/PandaTechAM/be-lib-analyzers</RepositoryUrl>
27-
<PackageReleaseNotes>Small fix</PackageReleaseNotes>
27+
<PackageReleaseNotes>Async rules final cleanup and bug fixes</PackageReleaseNotes>
2828

2929

3030
</PropertyGroup>

src/Analyzers/Async/AsyncHelpers.cs

Lines changed: 149 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
using System.Linq;
1+
using System;
2+
using System.Linq;
23
using Microsoft.CodeAnalysis;
4+
using Microsoft.CodeAnalysis.CSharp.Syntax;
5+
using Microsoft.CodeAnalysis.Operations;
36

47
namespace Pandatech.Analyzers.Async;
58

@@ -25,18 +28,160 @@ internal static bool IsContractImplementation(this IMethodSymbol method)
2528

2629
foreach (var member in containingType.AllInterfaces.SelectMany(iface => iface.GetMembers(method.Name)))
2730
{
28-
if (member is not IMethodSymbol ifaceMethod)
31+
if (member is not IMethodSymbol interfaceMethod)
2932
{
3033
continue;
3134
}
3235

33-
var implementation = containingType.FindImplementationForInterfaceMember(ifaceMethod);
34-
if (SymbolEqualityComparer.Default.Equals(implementation, method))
36+
var impl = containingType.FindImplementationForInterfaceMember(interfaceMethod);
37+
if (SymbolEqualityComparer.Default.Equals(impl, method))
3538
{
3639
return true;
3740
}
3841
}
3942

4043
return false;
4144
}
45+
46+
internal static bool HasUsing(this CompilationUnitSyntax root, string namespaceName)
47+
{
48+
return root.Usings.Any(u => u.Name?.ToString() == namespaceName);
49+
}
50+
51+
internal static bool IsMinimalApiMapMethod(this IMethodSymbol method)
52+
{
53+
if (!method.IsExtensionMethod)
54+
{
55+
return false;
56+
}
57+
58+
var containingType = method.ContainingType;
59+
if (containingType is null || containingType.Name != "EndpointRouteBuilderExtensions")
60+
{
61+
return false;
62+
}
63+
64+
var ns = containingType.ContainingNamespace.ToDisplayString();
65+
if (!ns.StartsWith("Microsoft.AspNetCore.Builder", StringComparison.Ordinal))
66+
{
67+
return false;
68+
}
69+
70+
return method.Name is
71+
"MapGet" or
72+
"MapPost" or
73+
"MapPut" or
74+
"MapDelete" or
75+
"MapPatch" or
76+
"MapHead" or
77+
"MapOptions" or
78+
"MapTrace" or
79+
"MapMethods";
80+
}
81+
82+
internal static IAnonymousFunctionOperation? ExtractAnonymousFunction(IOperation value)
83+
{
84+
while (true)
85+
{
86+
switch (value)
87+
{
88+
case IAnonymousFunctionOperation anon:
89+
return anon;
90+
91+
case IDelegateCreationOperation { Target: IAnonymousFunctionOperation anon }:
92+
return anon;
93+
94+
case IConversionOperation { Operand: var operand }:
95+
value = operand;
96+
continue;
97+
98+
default:
99+
return null;
100+
}
101+
}
102+
}
103+
104+
extension(ITypeSymbol type)
105+
{
106+
internal bool IsTaskLike()
107+
{
108+
if (type is not INamedTypeSymbol named)
109+
{
110+
return false;
111+
}
112+
113+
return named.ContainingNamespace.ToDisplayString() == "System.Threading.Tasks" &&
114+
named.Name is "Task" or "ValueTask";
115+
}
116+
117+
internal bool IsCancellationToken()
118+
{
119+
if (type is not INamedTypeSymbol named)
120+
{
121+
return false;
122+
}
123+
124+
return named.ContainingNamespace.ToDisplayString() == "System.Threading" &&
125+
named.Name == "CancellationToken";
126+
}
127+
}
128+
129+
extension(IMethodSymbol method)
130+
{
131+
internal bool IsTestMethod()
132+
{
133+
foreach (var attr in method.GetAttributes())
134+
{
135+
var attrClass = attr.AttributeClass;
136+
if (attrClass is null)
137+
{
138+
continue;
139+
}
140+
141+
var name = attrClass.Name;
142+
if (name.EndsWith("Attribute", StringComparison.Ordinal))
143+
{
144+
name = name.Substring(0, name.Length - "Attribute".Length);
145+
}
146+
147+
if (name is "Fact"
148+
or "Theory"
149+
or "Test"
150+
or "TestCase"
151+
or "TestMethod"
152+
or "DataTestMethod")
153+
{
154+
return true;
155+
}
156+
}
157+
158+
return false;
159+
}
160+
161+
internal bool IsMiddleware()
162+
{
163+
if (method.Name is not ("Invoke" or "InvokeAsync"))
164+
{
165+
return false;
166+
}
167+
168+
if (method.Parameters.Length == 0)
169+
{
170+
return false;
171+
}
172+
173+
if (method.ReturnType is not INamedTypeSymbol returnType || !returnType.IsTaskLike())
174+
{
175+
return false;
176+
}
177+
178+
if (method.Parameters[0].Type is not INamedTypeSymbol httpContextType)
179+
{
180+
return false;
181+
}
182+
183+
return httpContextType.Name == "HttpContext" &&
184+
httpContextType.ContainingNamespace.ToDisplayString() == "Microsoft.AspNetCore.Http";
185+
}
186+
}
42187
}

0 commit comments

Comments
 (0)