Skip to content

Commit 0a2b4d1

Browse files
committed
xunit/xunit#3169: xUnit3001 should not trigger on abstract classes
1 parent 2623373 commit 0a2b4d1

File tree

2 files changed

+64
-22
lines changed

2 files changed

+64
-22
lines changed

src/xunit.analyzers.tests/Analyzers/X3000/SerializableClassMustHaveParameterlessConstructorTests.cs

Lines changed: 62 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public class SerializedWithImplicitCtor { }
2525
public class SerializedWithExplicitCtor {
2626
public SerializedWithExplicitCtor() { }
2727
}
28-
28+
2929
[JsonTypeID("3")]
3030
public class {|#0:SerializedWithNoMatchingCtor|} {
3131
public SerializedWithNoMatchingCtor(int _) { }
@@ -53,7 +53,7 @@ public class RunnerReporter
5353
using Xunit.Runner.Common;
5454
using Xunit.Sdk;
5555
56-
public class {{|#0:MyRunnerReporter|}} : IRunnerReporter
56+
public {1} {{|#0:MyRunnerReporter|}} : IRunnerReporter
5757
{{
5858
{0}
5959
@@ -73,35 +73,47 @@ public ValueTask<IRunnerReporterMessageHandler> CreateMessageHandler(
7373
[Fact]
7474
public async Task ImplicitConstructor_DoesNotTrigger()
7575
{
76-
var source = string.Format(Template, string.Empty);
76+
var source = string.Format(Template, string.Empty, "class");
7777

7878
await VerifyV3.VerifyAnalyzerV3(LanguageVersion.CSharp8, source);
7979
}
8080

8181
[Fact]
8282
public async Task WrongConstructor_Triggers()
8383
{
84-
var source = string.Format(Template, /* lang=c#-test */ "public MyRunnerReporter(int x) { }");
84+
var source = string.Format(Template, /* lang=c#-test */ "public MyRunnerReporter(int x) { }", "class");
8585
var expected = VerifyV3.Diagnostic().WithLocation(0).WithArguments("MyRunnerReporter", "Xunit.Runner.Common.IRunnerReporter");
8686

8787
await VerifyV3.VerifyAnalyzerV3(LanguageVersion.CSharp8, source, expected);
8888
}
8989

90+
[Fact]
91+
public async Task WrongConstructorOnAbstractClass_DoesNotTrigger()
92+
{
93+
var source = string.Format(Template, /* lang=c#-test */ "public MyRunnerReporter(int x) { }", "abstract class");
94+
95+
await VerifyV3.VerifyAnalyzerV3(LanguageVersion.CSharp8, source);
96+
}
97+
9098
[Fact]
9199
public async Task NonPublicConstructor_Triggers()
92100
{
93-
var source = string.Format(Template, /* lang=c#-test */ "protected MyRunnerReporter() { }");
101+
var source = string.Format(Template, /* lang=c#-test */ "protected MyRunnerReporter() { }", "class");
94102
var expected = VerifyV3.Diagnostic().WithLocation(0).WithArguments("MyRunnerReporter", "Xunit.Runner.Common.IRunnerReporter");
95103

96104
await VerifyV3.VerifyAnalyzerV3(LanguageVersion.CSharp8, source, expected);
97105
}
98106

99107
[Fact]
100-
public async Task PublicParameterlessConstructor_DoesNotTrigger()
108+
public async Task NonPublicConstructorOnAbstractClass_DoesNotTrigger()
101109
{
102-
var source = string.Format(Template, /* lang=c#-test */ "public MyRunnerReporter() { }");
110+
await VerifyV3.VerifyAnalyzerV3(LanguageVersion.CSharp8, string.Format(Template, /* lang=c#-test */ "protected MyRunnerReporter() { }", "abstract class"));
111+
}
103112

104-
await VerifyV3.VerifyAnalyzerV3(LanguageVersion.CSharp8, source);
113+
[Fact]
114+
public async Task PublicParameterlessConstructor_DoesNotTrigger()
115+
{
116+
await VerifyV3.VerifyAnalyzerV3(LanguageVersion.CSharp8, string.Format(Template, /* lang=c#-test */ "public MyRunnerReporter() { }", "class"));
105117
}
106118
}
107119

@@ -111,7 +123,7 @@ public class XunitSerializable
111123
using {2};
112124
113125
public interface IMySerializer : IXunitSerializable {{ }}
114-
public class {{|#0:Foo|}} : {0}
126+
public {3} {{|#0:Foo|}} : {0}
115127
{{
116128
{1}
117129
public void Deserialize(IXunitSerializationInfo info) {{ }}
@@ -128,46 +140,62 @@ public void Serialize(IXunitSerializationInfo info) {{ }}
128140
[MemberData(nameof(Interfaces))]
129141
public async Task ImplicitConstructors_DoesNotTrigger(string @interface)
130142
{
131-
await VerifyV2.VerifyAnalyzerV2(string.Format(Template, @interface, "", "Xunit.Abstractions"));
132-
await VerifyV3.VerifyAnalyzerV3(string.Format(Template, @interface, "", "Xunit.Sdk"));
143+
await VerifyV2.VerifyAnalyzerV2(string.Format(Template, @interface, "", "Xunit.Abstractions", "class"));
144+
await VerifyV3.VerifyAnalyzerV3(string.Format(Template, @interface, "", "Xunit.Sdk", "class"));
133145
}
134146

135147
[Theory]
136148
[MemberData(nameof(Interfaces))]
137149
public async Task WrongConstructor_Triggers(string @interface)
138150
{
139-
var v2Source = string.Format(Template, @interface, /* lang=c#-test */ "public Foo(int x) { }", "Xunit.Abstractions");
151+
var v2Source = string.Format(Template, @interface, /* lang=c#-test */ "public Foo(int x) { }", "Xunit.Abstractions", "class");
140152
var v2Expected = VerifyV2.Diagnostic().WithLocation(0).WithArguments("Foo", "Xunit.Abstractions.IXunitSerializable");
141153

142154
await VerifyV2.VerifyAnalyzerV2(v2Source, v2Expected);
143155

144-
var v3Source = string.Format(Template, @interface, /* lang=c#-test */ "public Foo(int x) { }", "Xunit.Sdk");
156+
var v3Source = string.Format(Template, @interface, /* lang=c#-test */ "public Foo(int x) { }", "Xunit.Sdk", "class");
145157
var v3Expected = VerifyV3.Diagnostic().WithLocation(0).WithArguments("Foo", "Xunit.Sdk.IXunitSerializable");
146158

147159
await VerifyV3.VerifyAnalyzerV3(v3Source, v3Expected);
148160
}
149161

162+
[Theory]
163+
[MemberData(nameof(Interfaces))]
164+
public async Task WrongConstructorOnAbstractClass_DoesNotTrigger(string @interface)
165+
{
166+
await VerifyV2.VerifyAnalyzerV2(string.Format(Template, @interface, /* lang=c#-test */ "public Foo(int x) { }", "Xunit.Abstractions", "abstract class"));
167+
await VerifyV3.VerifyAnalyzerV3(string.Format(Template, @interface, /* lang=c#-test */ "public Foo(int x) { }", "Xunit.Sdk", "abstract class"));
168+
}
169+
150170
[Theory]
151171
[MemberData(nameof(Interfaces))]
152172
public async Task NonPublicConstructor_Triggers(string @interface)
153173
{
154-
var v2Source = string.Format(Template, @interface, /* lang=c#-test */ "protected Foo() { }", "Xunit.Abstractions");
174+
var v2Source = string.Format(Template, @interface, /* lang=c#-test */ "protected Foo() { }", "Xunit.Abstractions", "class");
155175
var v2Expected = VerifyV2.Diagnostic().WithLocation(0).WithArguments("Foo", "Xunit.Abstractions.IXunitSerializable");
156176

157177
await VerifyV2.VerifyAnalyzerV2(v2Source, v2Expected);
158178

159-
var v3Source = string.Format(Template, @interface, /* lang=c#-test */ "protected Foo() { }", "Xunit.Sdk");
179+
var v3Source = string.Format(Template, @interface, /* lang=c#-test */ "protected Foo() { }", "Xunit.Sdk", "class");
160180
var v3Expected = VerifyV3.Diagnostic().WithLocation(0).WithArguments("Foo", "Xunit.Sdk.IXunitSerializable");
161181

162182
await VerifyV3.VerifyAnalyzerV3(v3Source, v3Expected);
163183
}
164184

185+
[Theory]
186+
[MemberData(nameof(Interfaces))]
187+
public async Task NonPublicConstructorOnAbstractClass_DoesNotTrigger(string @interface)
188+
{
189+
await VerifyV2.VerifyAnalyzerV2(string.Format(Template, @interface, /* lang=c#-test */ "protected Foo() { }", "Xunit.Abstractions", "abstract class"));
190+
await VerifyV3.VerifyAnalyzerV3(string.Format(Template, @interface, /* lang=c#-test */ "protected Foo() { }", "Xunit.Sdk", "abstract class"));
191+
}
192+
165193
[Theory]
166194
[MemberData(nameof(Interfaces))]
167195
public async Task PublicParameterlessConstructor_DoesNotTrigger(string @interface)
168196
{
169-
await VerifyV2.VerifyAnalyzerV2(string.Format(Template, @interface, "public Foo() { }", "Xunit.Abstractions"));
170-
await VerifyV3.VerifyAnalyzerV3(string.Format(Template, @interface, "public Foo() { }", "Xunit.Sdk"));
197+
await VerifyV2.VerifyAnalyzerV2(string.Format(Template, @interface, /* lang=c#-test */ "public Foo() { }", "Xunit.Abstractions", "class"));
198+
await VerifyV3.VerifyAnalyzerV3(string.Format(Template, @interface, /* lang=c#-test */ "public Foo() { }", "Xunit.Sdk", "class"));
171199
}
172200
}
173201

@@ -177,7 +205,7 @@ public class XunitSerializer
177205
using System;
178206
using Xunit.Sdk;
179207
180-
public class {{|#0:MySerializer|}} : IXunitSerializer
208+
public {1} {{|#0:MySerializer|}} : IXunitSerializer
181209
{{
182210
{0}
183211
@@ -194,33 +222,45 @@ public bool IsSerializable(Type type, object? value, out string? failureReason)
194222
[Fact]
195223
public async Task ImplicitConstructor_DoesNotTrigger()
196224
{
197-
var source = string.Format(Template, string.Empty);
225+
var source = string.Format(Template, string.Empty, "class");
198226

199227
await VerifyV3.VerifyAnalyzerV3(LanguageVersion.CSharp8, source);
200228
}
201229

202230
[Fact]
203231
public async Task WrongConstructor_Triggers()
204232
{
205-
var source = string.Format(Template, /* lang=c#-test */ "public MySerializer(int x) { }");
233+
var source = string.Format(Template, /* lang=c#-test */ "public MySerializer(int x) { }", "class");
206234
var expected = VerifyV3.Diagnostic().WithLocation(0).WithArguments("MySerializer", "Xunit.Sdk.IXunitSerializer");
207235

208236
await VerifyV3.VerifyAnalyzerV3(LanguageVersion.CSharp8, source, expected);
209237
}
210238

239+
[Fact]
240+
public async Task WrongConstructorOnAbstractClass_DoesNotTrigger()
241+
{
242+
await VerifyV3.VerifyAnalyzerV3(LanguageVersion.CSharp8, string.Format(Template, /* lang=c#-test */ "public MySerializer(int x) { }", "abstract class"));
243+
}
244+
211245
[Fact]
212246
public async Task NonPublicConstructor_Triggers()
213247
{
214-
var source = string.Format(Template, /* lang=c#-test */ "protected MySerializer() { }");
248+
var source = string.Format(Template, /* lang=c#-test */ "protected MySerializer() { }", "class");
215249
var expected = VerifyV3.Diagnostic().WithLocation(0).WithArguments("MySerializer", "Xunit.Sdk.IXunitSerializer");
216250

217251
await VerifyV3.VerifyAnalyzerV3(LanguageVersion.CSharp8, source, expected);
218252
}
219253

254+
[Fact]
255+
public async Task NonPublicConstructorOnAbstractClass_DoesNotTrigger()
256+
{
257+
await VerifyV3.VerifyAnalyzerV3(LanguageVersion.CSharp8, string.Format(Template, /* lang=c#-test */ "protected MySerializer() { }", "abstract class"));
258+
}
259+
220260
[Fact]
221261
public async Task PublicParameterlessConstructor_DoesNotTrigger()
222262
{
223-
var source = string.Format(Template, /* lang=c#-test */ "public MySerializer() { }");
263+
var source = string.Format(Template, /* lang=c#-test */ "public MySerializer() { }", "class");
224264

225265
await VerifyV3.VerifyAnalyzerV3(LanguageVersion.CSharp8, source);
226266
}

src/xunit.analyzers/X3000/SerializableClassMustHaveParameterlessConstructor.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ public override void AnalyzeCompilation(
2525
return;
2626
if (namedType.TypeKind != TypeKind.Class)
2727
return;
28+
if (namedType.IsAbstract)
29+
return;
2830

2931
var serializableTargetDisplay = GetSerializableTargetDisplay(xunitContext, namedType);
3032
if (serializableTargetDisplay is null)

0 commit comments

Comments
 (0)