Skip to content

Commit 3560857

Browse files
committed
Inheritance works except with interfaces
1 parent 343d806 commit 3560857

File tree

2 files changed

+164
-1
lines changed

2 files changed

+164
-1
lines changed

VSDiagnostics/VSDiagnostics/VSDiagnostics.Test/Tests/Async/AsyncMethodWithoutAsyncSuffixTests.cs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,5 +1062,114 @@ public Task MyMethodAsync()
10621062
VerifyDiagnostic(original, diagnosticResultClass, diagnosticResultInterface);
10631063
VerifyFix(original, result);
10641064
}
1065+
1066+
[TestMethod]
1067+
public void AsyncMethodWithoutAsyncSuffix_WithVoidReturnType_InvokesWarning()
1068+
{
1069+
var original = @"
1070+
using System;
1071+
using System.Text;
1072+
using System.Threading.Tasks;
1073+
1074+
namespace ConsoleApplication1
1075+
{
1076+
class MyClass
1077+
{
1078+
async void Method()
1079+
{
1080+
1081+
}
1082+
}
1083+
}";
1084+
1085+
var result = @"
1086+
using System;
1087+
using System.Text;
1088+
using System.Threading.Tasks;
1089+
1090+
namespace ConsoleApplication1
1091+
{
1092+
class MyClass
1093+
{
1094+
async void MethodAsync()
1095+
{
1096+
1097+
}
1098+
}
1099+
}";
1100+
1101+
VerifyDiagnostic(original, string.Format(AsyncMethodWithoutAsyncSuffixAnalyzer.Rule.MessageFormat.ToString(), "Method"));
1102+
VerifyFix(original, result);
1103+
}
1104+
1105+
[TestMethod]
1106+
public void AsyncMethodWithoutAsyncSuffix_DefinedInBaseClass_WithOverriddenMember_WithLongerInheritanceStructure_WithMultipleAbstractClasses_InvokesWarning()
1107+
{
1108+
var original = @"
1109+
using System;
1110+
using System.Text;
1111+
using System.Threading.Tasks;
1112+
1113+
namespace ConsoleApplication1
1114+
{
1115+
abstract class OtherBaseClass
1116+
{
1117+
public abstract Task MyMethod();
1118+
}
1119+
1120+
abstract class BaseClass : OtherBaseClass
1121+
{
1122+
1123+
}
1124+
1125+
class MyClass : BaseClass
1126+
{
1127+
public override Task MyMethod()
1128+
{
1129+
return Task.CompletedTask;
1130+
}
1131+
}
1132+
}";
1133+
1134+
var result = @"
1135+
using System;
1136+
using System.Text;
1137+
using System.Threading.Tasks;
1138+
1139+
namespace ConsoleApplication1
1140+
{
1141+
abstract class OtherBaseClass
1142+
{
1143+
public abstract Task MyMethodAsync();
1144+
}
1145+
1146+
abstract class BaseClass : OtherBaseClass
1147+
{
1148+
1149+
}
1150+
1151+
class MyClass : BaseClass
1152+
{
1153+
public override Task MyMethodAsync()
1154+
{
1155+
return Task.CompletedTask;
1156+
}
1157+
}
1158+
}";
1159+
1160+
var diagnosticResultClass = new DiagnosticResult
1161+
{
1162+
Id = AsyncMethodWithoutAsyncSuffixAnalyzer.Rule.Id,
1163+
Message = string.Format(AsyncMethodWithoutAsyncSuffixAnalyzer.Rule.MessageFormat.ToString(), "MyMethod"),
1164+
Severity = AsyncMethodWithoutAsyncSuffixAnalyzer.Rule.DefaultSeverity,
1165+
Locations = new[]
1166+
{
1167+
new DiagnosticResultLocation("Test0.cs", 10, 31)
1168+
}
1169+
};
1170+
1171+
VerifyDiagnostic(original, diagnosticResultClass);
1172+
VerifyFix(original, result);
1173+
}
10651174
}
10661175
}

VSDiagnostics/VSDiagnostics/VSDiagnostics/Diagnostics/Async/AsyncMethodWithoutAsyncSuffix/AsyncMethodWithoutAsyncSuffixAnalyzer.cs

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Collections.Immutable;
2+
using System.Linq;
23
using System.Threading.Tasks;
34
using Microsoft.CodeAnalysis;
45
using Microsoft.CodeAnalysis.CSharp;
@@ -45,13 +46,66 @@ private static void AnalyzeSyntaxNode(SyntaxNodeAnalysisContext context)
4546
return;
4647
}
4748

48-
if (method.Modifiers.Any(SyntaxKind.AsyncKeyword) || returnType.Type.MetadataName == typeof(Task).Name || returnType.Type.MetadataName == typeof(Task<>).Name)
49+
var declaredSymbol = context.SemanticModel.GetDeclaredSymbol(method);
50+
if (declaredSymbol == null)
51+
{
52+
return;
53+
}
54+
55+
if (IsDefinedInAncestor(declaredSymbol, context))
56+
{
57+
return;
58+
}
59+
60+
if (method.Modifiers.Any(SyntaxKind.AsyncKeyword) ||
61+
returnType.Type.MetadataName == typeof(Task).Name ||
62+
returnType.Type.MetadataName == typeof(Task<>).Name)
4963
{
5064
if (!method.Identifier.Text.EndsWith("Async"))
5165
{
5266
context.ReportDiagnostic(Diagnostic.Create(Rule, method.Identifier.GetLocation(), method.Identifier.Text));
5367
}
5468
}
5569
}
70+
71+
private static bool IsDefinedInAncestor(IMethodSymbol methodSymbol, SyntaxNodeAnalysisContext context)
72+
{
73+
var type = methodSymbol?.ContainingType;
74+
if (type == null)
75+
{
76+
return false;
77+
}
78+
79+
var interfaces = type.AllInterfaces;
80+
foreach (var @interface in interfaces)
81+
{
82+
var interfaceMethods = @interface.GetMembers().OfType<IMethodSymbol>().ToArray();
83+
foreach (var method in interfaceMethods)
84+
{
85+
if (method.Equals(methodSymbol))
86+
{
87+
return true;
88+
}
89+
}
90+
}
91+
92+
// Start with the first ancestor
93+
type = type.BaseType;
94+
while (type != null)
95+
{
96+
var methods = type.GetMembers().OfType<IMethodSymbol>().ToArray();
97+
foreach (var method in methods)
98+
{
99+
if (method.Equals(methodSymbol))
100+
{
101+
return true;
102+
}
103+
}
104+
105+
type = type.BaseType;
106+
}
107+
108+
return false;
109+
}
56110
}
57111
}

0 commit comments

Comments
 (0)