From 3161da3369be368ffacd9e47e8880f80393c92f1 Mon Sep 17 00:00:00 2001 From: Richard Murillo Date: Mon, 26 Aug 2024 12:22:38 -0700 Subject: [PATCH] Update detection logic to see if types are assignable rather than strictly equal --- ...ignatureShouldMatchMockedMethodAnalyzer.cs | 29 +++++++++++++++---- ...tureShouldMatchMockedMethodCodeFixTests.cs | 29 +++++++++++++++---- 2 files changed, 47 insertions(+), 11 deletions(-) diff --git a/src/Moq.Analyzers/CallbackSignatureShouldMatchMockedMethodAnalyzer.cs b/src/Moq.Analyzers/CallbackSignatureShouldMatchMockedMethodAnalyzer.cs index ead2b873..d5695217 100644 --- a/src/Moq.Analyzers/CallbackSignatureShouldMatchMockedMethodAnalyzer.cs +++ b/src/Moq.Analyzers/CallbackSignatureShouldMatchMockedMethodAnalyzer.cs @@ -74,13 +74,32 @@ private static void Analyze(SyntaxNodeAnalysisContext context) TypeInfo mockedMethodArgumentType = context.SemanticModel.GetTypeInfo(mockedMethodArguments[argumentIndex].Expression, context.CancellationToken); - string? mockedMethodTypeName = mockedMethodArgumentType.ConvertedType?.ToString(); - string? lambdaParameterTypeName = lambdaParameterType.ConvertedType?.ToString(); - if (!string.Equals(mockedMethodTypeName, lambdaParameterTypeName, StringComparison.Ordinal)) + + // Check if types are assignable rather than strictly equal + ITypeSymbol? lambdaParameterTypeSymbol = lambdaParameterType.Type; + ITypeSymbol? mockedMethodTypeSymbol = mockedMethodArgumentType.Type; + + if (lambdaParameterTypeSymbol is not null && mockedMethodTypeSymbol is not null) + { + + if (!context.SemanticModel.Compilation.ClassifyConversion(lambdaParameterTypeSymbol, mockedMethodTypeSymbol).IsImplicit) + { + + Diagnostic diagnostic = lambdaParameters[argumentIndex].GetLocation().CreateDiagnostic(Rule); + context.ReportDiagnostic(diagnostic); + } + } + else { - Diagnostic diagnostic = callbackLambda.ParameterList.CreateDiagnostic(Rule); - context.ReportDiagnostic(diagnostic); + string? mockedMethodTypeName = mockedMethodArgumentType.ConvertedType?.ToString(); + string? lambdaParameterTypeName = lambdaParameterType.ConvertedType?.ToString(); + + if (!string.Equals(mockedMethodTypeName, lambdaParameterTypeName, StringComparison.Ordinal)) + { + Diagnostic diagnostic = callbackLambda.ParameterList.CreateDiagnostic(Rule); + context.ReportDiagnostic(diagnostic); + } } } } diff --git a/tests/Moq.Analyzers.Test/CallbackSignatureShouldMatchMockedMethodCodeFixTests.cs b/tests/Moq.Analyzers.Test/CallbackSignatureShouldMatchMockedMethodCodeFixTests.cs index 18370a91..7baa2f21 100644 --- a/tests/Moq.Analyzers.Test/CallbackSignatureShouldMatchMockedMethodCodeFixTests.cs +++ b/tests/Moq.Analyzers.Test/CallbackSignatureShouldMatchMockedMethodCodeFixTests.cs @@ -1,9 +1,17 @@ +using Xunit.Abstractions; using Verifier = Moq.Analyzers.Test.Helpers.CodeFixVerifier; namespace Moq.Analyzers.Test; public class CallbackSignatureShouldMatchMockedMethodCodeFixTests { + private readonly ITestOutputHelper _output; + + public CallbackSignatureShouldMatchMockedMethodCodeFixTests(ITestOutputHelper output) + { + _output = output; + } + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "MA0051:Method is too long", Justification = "Contains test data")] public static IEnumerable TestData() { @@ -22,7 +30,7 @@ public static IEnumerable TestData() """new Mock().Setup(x => x.Do(It.IsAny>())).Returns((List l) => { return 0; });""", ], [ - """new Mock().Setup(x => x.Do(It.IsAny())).Callback({|Moq1100:(int i)|} => { });""", + """new Mock().Setup(x => x.Do(It.IsAny())).Callback(({|Moq1100:int i|}) => { });""", """new Mock().Setup(x => x.Do(It.IsAny())).Callback((string s) => { });""", ], [ @@ -34,7 +42,7 @@ public static IEnumerable TestData() """new Mock().Setup(x => x.Do(It.IsAny(), It.IsAny(), It.IsAny())).Callback((int i, string s, DateTime dt) => { });""", ], [ - """new Mock().Setup(x => x.Do(It.IsAny>())).Callback({|Moq1100:(int i)|} => { });""", + """new Mock().Setup(x => x.Do(It.IsAny>())).Callback(({|Moq1100:int i|}) => { });""", """new Mock().Setup(x => x.Do(It.IsAny>())).Callback((List l) => { });""", ], [ @@ -74,8 +82,8 @@ public static IEnumerable TestData() """new Mock().Setup(x => x.Do(It.IsAny>())).Returns(0).Callback((List l) => { });""", ], [ // Repro for https://github.com/rjmurillo/moq.analyzers/issues/172 - """new Mock().Setup(m => m.DoSomething(It.IsAny())).Returns((object? bar) => true);""", - """new Mock().Setup(m => m.DoSomething(It.IsAny())).Returns((object? bar) => true);""", + """new Mock().Setup(m => m.Do(It.IsAny())).Returns((object? bar) => true);""", + """new Mock().Setup(m => m.Do(It.IsAny())).Returns((object? bar) => true);""", ], }.WithNamespaces().WithMoqReferenceAssemblyGroups(); } @@ -96,7 +104,7 @@ internal interface IFoo int Do(List l); - bool DoSomething(object? bar); + bool Do(object? bar); } internal class UnitTest @@ -108,6 +116,15 @@ private void Test() } """; - await Verifier.VerifyCodeFixAsync(Template(@namespace, original), Template(@namespace, quickFix), referenceAssemblyGroup); + string o = Template(@namespace, original); + string f = Template(@namespace, quickFix); + + _output.WriteLine("Original:"); + _output.WriteLine(o); + _output.WriteLine(string.Empty); + _output.WriteLine("Fixed:"); + _output.WriteLine(f); + + await Verifier.VerifyCodeFixAsync(o, f, referenceAssemblyGroup); } }