diff --git a/src/xunit.analyzers.fixes/X3000/SerializableClassMustHaveParameterlessConstructorFixer.cs b/src/xunit.analyzers.fixes/X3000/SerializableClassMustHaveParameterlessConstructorFixer.cs index 1d6c0eab..0ddd39bf 100644 --- a/src/xunit.analyzers.fixes/X3000/SerializableClassMustHaveParameterlessConstructorFixer.cs +++ b/src/xunit.analyzers.fixes/X3000/SerializableClassMustHaveParameterlessConstructorFixer.cs @@ -62,7 +62,7 @@ static async Task CreateOrUpdateConstructor( var newCtor = generator.ConstructorDeclaration(); newCtor = generator.WithAccessibility(newCtor, Accessibility.Public); newCtor = generator.AddAttributes(newCtor, obsoleteAttribute); - editor.InsertMembers(declaration, 0, new[] { newCtor }); + editor.InsertMembers(declaration, 0, [newCtor]); } else { diff --git a/src/xunit.analyzers.tests/Analyzers/X3000/SerializableClassMustHaveParameterlessConstructorTests.cs b/src/xunit.analyzers.tests/Analyzers/X3000/SerializableClassMustHaveParameterlessConstructorTests.cs index 902267f7..55e7d67c 100644 --- a/src/xunit.analyzers.tests/Analyzers/X3000/SerializableClassMustHaveParameterlessConstructorTests.cs +++ b/src/xunit.analyzers.tests/Analyzers/X3000/SerializableClassMustHaveParameterlessConstructorTests.cs @@ -2,12 +2,13 @@ using Microsoft.CodeAnalysis; using Xunit; using Xunit.Analyzers; -using Verify = CSharpVerifier; +using VerifyV2 = CSharpVerifier; +using VerifyV3 = CSharpVerifier; public class SerializableClassMustHaveParameterlessConstructorTests { static readonly string Template = @" -using Xunit.Abstractions; +using {2}; public interface IMySerializer : IXunitSerializable {{ }} public class Foo : {0} @@ -26,51 +27,83 @@ public void Serialize(IXunitSerializationInfo info) {{ }} [MemberData(nameof(Interfaces))] public async Task ImplicitConstructors_NoDiagnostics(string @interface) { - var source = string.Format(Template, @interface, ""); + var v2Source = string.Format(Template, @interface, "", "Xunit.Abstractions"); - await Verify.VerifyAnalyzerV2(source); + await VerifyV2.VerifyAnalyzerV2(v2Source); + + var v3Source = string.Format(Template, @interface, "", "Xunit.Sdk"); + + await VerifyV3.VerifyAnalyzerV3(v3Source); } [Theory] [MemberData(nameof(Interfaces))] public async Task WrongConstructor_ReturnsError(string @interface) { - var source = string.Format(Template, @interface, "public Foo(int x) { }"); - var expected = - Verify + var v2Source = string.Format(Template, @interface, "public Foo(int x) { }", "Xunit.Abstractions"); + var v2Expected = + VerifyV2 .Diagnostic() .WithSpan(5, 14, 5, 17) .WithArguments("Foo"); - await Verify.VerifyAnalyzerV2(source, expected); + await VerifyV2.VerifyAnalyzerV2(v2Source, v2Expected); + + var v3Source = string.Format(Template, @interface, "public Foo(int x) { }", "Xunit.Sdk"); + var v3Expected = + VerifyV3 + .Diagnostic() + .WithSpan(5, 14, 5, 17) + .WithArguments("Foo"); + + await VerifyV3.VerifyAnalyzerV3(v3Source, v3Expected); } [Theory] [MemberData(nameof(Interfaces))] public async Task NonPublicConstructor_ReturnsError(string @interface) { - var source = string.Format(Template, @interface, "protected Foo() { }"); - var expected = - Verify + var v2Source = string.Format(Template, @interface, "protected Foo() { }", "Xunit.Abstractions"); + var v2Expected = + VerifyV2 + .Diagnostic() + .WithSpan(5, 14, 5, 17) + .WithArguments("Foo"); + + await VerifyV2.VerifyAnalyzerV2(v2Source, v2Expected); + + var v3Source = string.Format(Template, @interface, "protected Foo() { }", "Xunit.Sdk"); + var v3Expected = + VerifyV3 .Diagnostic() .WithSpan(5, 14, 5, 17) .WithArguments("Foo"); - await Verify.VerifyAnalyzerV2(source, expected); + await VerifyV3.VerifyAnalyzerV3(v3Source, v3Expected); } [Theory] [MemberData(nameof(Interfaces))] public async Task PublicParameterlessConstructor_NoDiagnostics(string @interface) { - var source = string.Format(Template, @interface, "public Foo() { }"); + var v2Source = string.Format(Template, @interface, "public Foo() { }", "Xunit.Abstractions"); + + await VerifyV2.VerifyAnalyzerV2(v2Source); + + var v3Source = string.Format(Template, @interface, "public Foo() { }", "Xunit.Sdk"); - await Verify.VerifyAnalyzerV2(source); + await VerifyV3.VerifyAnalyzerV3(v3Source); } - public class Analyzer : SerializableClassMustHaveParameterlessConstructor + public class V2Analyzer : SerializableClassMustHaveParameterlessConstructor { protected override XunitContext CreateXunitContext(Compilation compilation) => XunitContext.ForV2Abstractions(compilation); } + + public class V3Analyzer : SerializableClassMustHaveParameterlessConstructor + { + protected override XunitContext CreateXunitContext(Compilation compilation) => + XunitContext.ForV3Core(compilation); + } } diff --git a/src/xunit.analyzers.tests/Fixes/X3000/SerializableClassMustHaveParameterlessConstructorFixerTests.cs b/src/xunit.analyzers.tests/Fixes/X3000/SerializableClassMustHaveParameterlessConstructorFixerTests.cs index 6503c084..66970f81 100644 --- a/src/xunit.analyzers.tests/Fixes/X3000/SerializableClassMustHaveParameterlessConstructorFixerTests.cs +++ b/src/xunit.analyzers.tests/Fixes/X3000/SerializableClassMustHaveParameterlessConstructorFixerTests.cs @@ -8,109 +8,145 @@ public class SerializableClassMustHaveParameterlessConstructorFixerTests [Fact] public async Task WithPublicParameteredConstructor_AddsNewConstructor() { - var before = @" -public class [|MyTestCase|]: Xunit.Abstractions.IXunitSerializable { - public MyTestCase(int x) { } + var beforeTemplate = @" +public class [|MyTestCase|]: {0}.IXunitSerializable {{ + public MyTestCase(int x) {{ }} - void Xunit.Abstractions.IXunitSerializable.Deserialize(Xunit.Abstractions.IXunitSerializationInfo _) { } - void Xunit.Abstractions.IXunitSerializable.Serialize(Xunit.Abstractions.IXunitSerializationInfo _) { } -}"; + void {0}.IXunitSerializable.Deserialize({0}.IXunitSerializationInfo _) {{ }} + void {0}.IXunitSerializable.Serialize({0}.IXunitSerializationInfo _) {{ }} +}}"; - var after = @" -public class MyTestCase: Xunit.Abstractions.IXunitSerializable { + var afterTemplate = @" +public class MyTestCase: {0}.IXunitSerializable {{ [System.Obsolete(""Called by the de-serializer; should only be called by deriving classes for de-serialization purposes"")] public MyTestCase() - { - } + {{ + }} - public MyTestCase(int x) { } + public MyTestCase(int x) {{ }} - void Xunit.Abstractions.IXunitSerializable.Deserialize(Xunit.Abstractions.IXunitSerializationInfo _) { } - void Xunit.Abstractions.IXunitSerializable.Serialize(Xunit.Abstractions.IXunitSerializationInfo _) { } -}"; + void {0}.IXunitSerializable.Deserialize({0}.IXunitSerializationInfo _) {{ }} + void {0}.IXunitSerializable.Serialize({0}.IXunitSerializationInfo _) {{ }} +}}"; - await Verify.VerifyCodeFixV2(before, after, SerializableClassMustHaveParameterlessConstructorFixer.Key_GenerateOrUpdateConstructor); + var v2Before = string.Format(beforeTemplate, "Xunit.Abstractions"); + var v2After = string.Format(afterTemplate, "Xunit.Abstractions"); + + await Verify.VerifyCodeFixV2(v2Before, v2After, SerializableClassMustHaveParameterlessConstructorFixer.Key_GenerateOrUpdateConstructor); + + var v3Before = string.Format(beforeTemplate, "Xunit.Sdk"); + var v3After = string.Format(afterTemplate, "Xunit.Sdk"); + + await Verify.VerifyCodeFixV3(v3Before, v3After, SerializableClassMustHaveParameterlessConstructorFixer.Key_GenerateOrUpdateConstructor); } [Fact] public async Task WithNonPublicParameterlessConstructor_ChangesVisibility_WithoutUsing() { - var before = @" -public class [|MyTestCase|]: Xunit.Abstractions.IXunitSerializable { - protected MyTestCase() { throw new System.DivideByZeroException(); } + var beforeTemplate = @" +using {0}; + +public class [|MyTestCase|]: IXunitSerializable {{ + protected MyTestCase() {{ throw new System.DivideByZeroException(); }} - void Xunit.Abstractions.IXunitSerializable.Deserialize(Xunit.Abstractions.IXunitSerializationInfo _) { } - void Xunit.Abstractions.IXunitSerializable.Serialize(Xunit.Abstractions.IXunitSerializationInfo _) { } -}"; + void IXunitSerializable.Deserialize(IXunitSerializationInfo _) {{ }} + void IXunitSerializable.Serialize(IXunitSerializationInfo _) {{ }} +}}"; - var after = @" -public class MyTestCase: Xunit.Abstractions.IXunitSerializable { + var afterTemplate = @" +using {0}; + +public class MyTestCase: IXunitSerializable {{ [System.Obsolete(""Called by the de-serializer; should only be called by deriving classes for de-serialization purposes"")] - public MyTestCase() { throw new System.DivideByZeroException(); } + public MyTestCase() {{ throw new System.DivideByZeroException(); }} + + void IXunitSerializable.Deserialize(IXunitSerializationInfo _) {{ }} + void IXunitSerializable.Serialize(IXunitSerializationInfo _) {{ }} +}}"; + + var v2Before = string.Format(beforeTemplate, "Xunit.Abstractions"); + var v2After = string.Format(afterTemplate, "Xunit.Abstractions"); - void Xunit.Abstractions.IXunitSerializable.Deserialize(Xunit.Abstractions.IXunitSerializationInfo _) { } - void Xunit.Abstractions.IXunitSerializable.Serialize(Xunit.Abstractions.IXunitSerializationInfo _) { } -}"; + await Verify.VerifyCodeFixV2(v2Before, v2After, SerializableClassMustHaveParameterlessConstructorFixer.Key_GenerateOrUpdateConstructor); - await Verify.VerifyCodeFixV2(before, after, SerializableClassMustHaveParameterlessConstructorFixer.Key_GenerateOrUpdateConstructor); + var v3Before = string.Format(beforeTemplate, "Xunit.Sdk"); + var v3After = string.Format(afterTemplate, "Xunit.Sdk"); + + await Verify.VerifyCodeFixV3(v3Before, v3After, SerializableClassMustHaveParameterlessConstructorFixer.Key_GenerateOrUpdateConstructor); } [Fact] public async Task WithNonPublicParameterlessConstructor_ChangesVisibility_WithUsing() { - var before = @" + var beforeTemplate = @" using System; -using Xunit.Abstractions; +using {0}; -public class [|MyTestCase|]: IXunitSerializable { - protected MyTestCase() { throw new DivideByZeroException(); } +public class [|MyTestCase|]: IXunitSerializable {{ + protected MyTestCase() {{ throw new DivideByZeroException(); }} - void IXunitSerializable.Deserialize(IXunitSerializationInfo _) { } - void IXunitSerializable.Serialize(IXunitSerializationInfo _) { } -}"; + void IXunitSerializable.Deserialize(IXunitSerializationInfo _) {{ }} + void IXunitSerializable.Serialize(IXunitSerializationInfo _) {{ }} +}}"; - var after = @" + var afterTemplate = @" using System; -using Xunit.Abstractions; +using {0}; -public class MyTestCase: IXunitSerializable { +public class MyTestCase: IXunitSerializable {{ [Obsolete(""Called by the de-serializer; should only be called by deriving classes for de-serialization purposes"")] - public MyTestCase() { throw new DivideByZeroException(); } + public MyTestCase() {{ throw new DivideByZeroException(); }} + + void IXunitSerializable.Deserialize(IXunitSerializationInfo _) {{ }} + void IXunitSerializable.Serialize(IXunitSerializationInfo _) {{ }} +}}"; + + var v2Before = string.Format(beforeTemplate, "Xunit.Abstractions"); + var v2After = string.Format(afterTemplate, "Xunit.Abstractions"); + + await Verify.VerifyCodeFixV2(v2Before, v2After, SerializableClassMustHaveParameterlessConstructorFixer.Key_GenerateOrUpdateConstructor); - void IXunitSerializable.Deserialize(IXunitSerializationInfo _) { } - void IXunitSerializable.Serialize(IXunitSerializationInfo _) { } -}"; + var v3Before = string.Format(beforeTemplate, "Xunit.Sdk"); + var v3After = string.Format(afterTemplate, "Xunit.Sdk"); - await Verify.VerifyCodeFixV2(before, after, SerializableClassMustHaveParameterlessConstructorFixer.Key_GenerateOrUpdateConstructor); + await Verify.VerifyCodeFixV3(v3Before, v3After, SerializableClassMustHaveParameterlessConstructorFixer.Key_GenerateOrUpdateConstructor); } [Fact] public async Task PreservesExistingObsoleteAttribute() { - var before = @" -using Xunit.Abstractions; + var beforeTemplate = @" +using {0}; using obo = System.ObsoleteAttribute; -public class [|MyTestCase|]: IXunitSerializable { +public class [|MyTestCase|]: IXunitSerializable {{ [obo(""This is my custom obsolete message"")] - protected MyTestCase() { throw new System.DivideByZeroException(); } + protected MyTestCase() {{ throw new System.DivideByZeroException(); }} - void IXunitSerializable.Deserialize(IXunitSerializationInfo _) { } - void IXunitSerializable.Serialize(IXunitSerializationInfo _) { } -}"; + void IXunitSerializable.Deserialize(IXunitSerializationInfo _) {{ }} + void IXunitSerializable.Serialize(IXunitSerializationInfo _) {{ }} +}}"; - var after = @" -using Xunit.Abstractions; + var afterTemplate = @" +using {0}; using obo = System.ObsoleteAttribute; -public class MyTestCase: IXunitSerializable { +public class MyTestCase: IXunitSerializable {{ [obo(""This is my custom obsolete message"")] - public MyTestCase() { throw new System.DivideByZeroException(); } + public MyTestCase() {{ throw new System.DivideByZeroException(); }} + + void IXunitSerializable.Deserialize(IXunitSerializationInfo _) {{ }} + void IXunitSerializable.Serialize(IXunitSerializationInfo _) {{ }} +}}"; + + var v2Before = string.Format(beforeTemplate, "Xunit.Abstractions"); + var v2After = string.Format(afterTemplate, "Xunit.Abstractions"); + + await Verify.VerifyCodeFixV2(v2Before, v2After, SerializableClassMustHaveParameterlessConstructorFixer.Key_GenerateOrUpdateConstructor); - void IXunitSerializable.Deserialize(IXunitSerializationInfo _) { } - void IXunitSerializable.Serialize(IXunitSerializationInfo _) { } -}"; + var v3Before = string.Format(beforeTemplate, "Xunit.Sdk"); + var v3After = string.Format(afterTemplate, "Xunit.Sdk"); - await Verify.VerifyCodeFixV2(before, after, SerializableClassMustHaveParameterlessConstructorFixer.Key_GenerateOrUpdateConstructor); + await Verify.VerifyCodeFixV3(v3Before, v3After, SerializableClassMustHaveParameterlessConstructorFixer.Key_GenerateOrUpdateConstructor); } } diff --git a/src/xunit.analyzers/X3000/SerializableClassMustHaveParameterlessConstructor.cs b/src/xunit.analyzers/X3000/SerializableClassMustHaveParameterlessConstructor.cs index 21038cc8..ee56cb87 100644 --- a/src/xunit.analyzers/X3000/SerializableClassMustHaveParameterlessConstructor.cs +++ b/src/xunit.analyzers/X3000/SerializableClassMustHaveParameterlessConstructor.cs @@ -5,7 +5,7 @@ namespace Xunit.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp)] -public class SerializableClassMustHaveParameterlessConstructor : XunitV2DiagnosticAnalyzer +public class SerializableClassMustHaveParameterlessConstructor : XunitDiagnosticAnalyzer { public SerializableClassMustHaveParameterlessConstructor() : base(Descriptors.X3001_SerializableClassMustHaveParameterlessConstructor) @@ -25,7 +25,7 @@ public override void AnalyzeCompilation( if (namedType.TypeKind != TypeKind.Class) return; - var isXunitSerializable = xunitContext.V2Abstractions?.IXunitSerializableType?.IsAssignableFrom(namedType) ?? false; + var isXunitSerializable = xunitContext.Abstractions.IXunitSerializableType?.IsAssignableFrom(namedType) ?? false; if (!isXunitSerializable) return; @@ -42,7 +42,4 @@ public override void AnalyzeCompilation( ); }, SymbolKind.NamedType); } - - protected override bool ShouldAnalyze(XunitContext xunitContext) => - Guard.ArgumentNotNull(xunitContext).V2Abstractions is not null; }