From 0c15399095e079676d3eb96fc59ff2aa43fdfca5 Mon Sep 17 00:00:00 2001 From: etherfield <148793545+etherfield@users.noreply.github.com> Date: Fri, 17 Nov 2023 18:33:17 -0800 Subject: [PATCH] Fixes xunit/xunit#2798: xUnit2007 code fix leads to CS8920 compiler error (#167) --- ...IsTypeShouldUseGenericOverloadTypeTests.cs | 86 +++++++++++++++++++ ...ssertIsTypeShouldUseGenericOverloadType.cs | 17 ++++ 2 files changed, 103 insertions(+) create mode 100644 src/xunit.analyzers.tests/Analyzers/X2000/AssertIsTypeShouldUseGenericOverloadTypeTests.cs diff --git a/src/xunit.analyzers.tests/Analyzers/X2000/AssertIsTypeShouldUseGenericOverloadTypeTests.cs b/src/xunit.analyzers.tests/Analyzers/X2000/AssertIsTypeShouldUseGenericOverloadTypeTests.cs new file mode 100644 index 00000000..2e2353eb --- /dev/null +++ b/src/xunit.analyzers.tests/Analyzers/X2000/AssertIsTypeShouldUseGenericOverloadTypeTests.cs @@ -0,0 +1,86 @@ +using Microsoft.CodeAnalysis.CSharp; +using Xunit; +using Verify = CSharpVerifier; + +public class AssertIsTypeShouldUseGenericOverloadTypeTests +{ +#if NETCOREAPP // static abstract methods are only supported on .NET, not .NET Framework + public class StaticAbstractInterfaceMethods + { + const string methodCode = "static abstract void Method();"; + const string codeTemplate = @" +using Xunit; + +public interface IParentClass {{ + {0} +}} + +public interface IClass : IParentClass {{ + {1} +}} + +public class Class : IClass {{ + public static void Method() {{ }} +}} + +public abstract class TestClass {{ + [Fact] + public void TestMethod() {{ + var data = new Class(); + + Assert.IsAssignableFrom(typeof(IClass), data); + }} +}}"; + + [Fact] + public async void DoesNotFindWarning_ForStaticAbstractInterfaceMembers() + { + string source = string.Format(codeTemplate, string.Empty, methodCode); + + await Verify.VerifyAnalyzer(LanguageVersion.Preview, source); + } + + [Fact] + public async void DoesNotFindWarning_ForNestedStaticAbstractInterfaceMembers() + { + string source = string.Format(codeTemplate, methodCode, string.Empty); + + await Verify.VerifyAnalyzer(LanguageVersion.Preview, source); + } + + [Theory] + [InlineData("static", "", "{ }")] + [InlineData("", "abstract", ";")] + public async void FindsWarning_ForNotStaticAbstractInterfaceMembers(string staticModifier, string abstractModifier, string methodBody) + { + string source = $@" +using Xunit; + +public interface IClass {{ + {staticModifier} {abstractModifier} void Method() {methodBody} +}} + +public class Class : IClass {{ + public {staticModifier} void Method() {{ }} +}} + +public abstract class TestClass {{ + [Fact] + public void TestMethod() {{ + var data = new Class(); + + Assert.IsAssignableFrom(typeof(IClass), data); + }} +}}"; + + var expected = + Verify + .Diagnostic() + .WithSpan(17, 9, 17, 54) + .WithArguments("IClass"); + + await Verify.VerifyAnalyzer(LanguageVersion.CSharp8, source, expected); + } + } +#endif +} diff --git a/src/xunit.analyzers/X2000/AssertIsTypeShouldUseGenericOverloadType.cs b/src/xunit.analyzers/X2000/AssertIsTypeShouldUseGenericOverloadType.cs index 4b925cbd..7c2bee8d 100644 --- a/src/xunit.analyzers/X2000/AssertIsTypeShouldUseGenericOverloadType.cs +++ b/src/xunit.analyzers/X2000/AssertIsTypeShouldUseGenericOverloadType.cs @@ -38,6 +38,23 @@ protected override void AnalyzeInvocation( var type = typeOfOperation.TypeOperand; var typeName = SymbolDisplay.ToDisplayString(type); + // Static abstract interface members can't be used as types in generics + if (type.TypeKind == TypeKind.Interface) + { + var allInterfaces = (type as INamedTypeSymbol)?.AllInterfaces; + if (allInterfaces != null) + { + var allMembers = + allInterfaces + .Value + .SelectMany(i => i.GetMembers()) + .Concat(type.GetMembers()); + + if (allMembers.Any(m => m is { IsAbstract: true, IsStatic: true })) + return; + } + } + var builder = ImmutableDictionary.CreateBuilder(); builder[Constants.Properties.MethodName] = method.Name; builder[Constants.Properties.TypeName] = typeName;