Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Moq1100 incorrectly firing on nullable parameters #187

Merged
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
02a8426
Add test case for #172
rjmurillo Aug 26, 2024
3161da3
Update detection logic to see if types are assignable rather than str…
rjmurillo Aug 26, 2024
da6de24
Refactor Analyzer to move bulk of parameter validation logic to new m…
rjmurillo Aug 26, 2024
dcd245d
Add additional repro cases for #172 (thanks @marcovr)
rjmurillo Aug 27, 2024
3701b6f
Update Diagnostic to use factory method
rjmurillo Aug 27, 2024
f28f271
Update type conversion detection logic to ensure a conversion of some…
rjmurillo Aug 27, 2024
8fcabe9
Add test case for #172
rjmurillo Aug 26, 2024
57c394c
Update detection logic to see if types are assignable rather than str…
rjmurillo Aug 26, 2024
1d53ee5
Refactor Analyzer to move bulk of parameter validation logic to new m…
rjmurillo Aug 26, 2024
67576f9
Add additional repro cases for #172 (thanks @marcovr)
rjmurillo Aug 27, 2024
ebb52b5
Update Diagnostic to use factory method
rjmurillo Aug 27, 2024
771794c
Update type conversion detection logic to ensure a conversion of some…
rjmurillo Aug 27, 2024
b9d53df
Improve comments about changes
rjmurillo Aug 27, 2024
eb7c722
Add support for user-defined implicit and explicit conversions
rjmurillo Aug 27, 2024
b4fde55
Update for merge conflicts
rjmurillo Aug 27, 2024
8388a83
Fix static analysis warnings
rjmurillo Aug 27, 2024
d028e35
Update SquiggleCop baseline
rjmurillo Aug 27, 2024
d53201c
Refactor check for conversion logic to own method and update document…
rjmurillo Aug 27, 2024
65e8beb
Add tests for different implicit and explicit conversion scenarios
rjmurillo Aug 27, 2024
7b16be7
Update SquiggleCop baseline
rjmurillo Aug 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -58,31 +58,52 @@ private static void Analyze(SyntaxNodeAnalysisContext context)

if (mockedMethodArguments.Count != lambdaParameters.Count)
{
Diagnostic diagnostic = Diagnostic.Create(Rule, callbackLambda.ParameterList.GetLocation());
Diagnostic diagnostic = callbackLambda.ParameterList.GetLocation().CreateDiagnostic(Rule);
context.ReportDiagnostic(diagnostic);
}
else
{
for (int argumentIndex = 0; argumentIndex < mockedMethodArguments.Count; argumentIndex++)
ValidateParameters(context, mockedMethodArguments, lambdaParameters);
}
}

private static void ValidateParameters(
rjmurillo marked this conversation as resolved.
Show resolved Hide resolved
SyntaxNodeAnalysisContext context,
SeparatedSyntaxList<ArgumentSyntax> mockedMethodArguments,
SeparatedSyntaxList<ParameterSyntax> lambdaParameters)
{
for (int argumentIndex = 0; argumentIndex < mockedMethodArguments.Count; argumentIndex++)
{
TypeSyntax? lambdaParameterTypeSyntax = lambdaParameters[argumentIndex].Type;

// TODO: Don't know if continue or break is the right thing to do here
rjmurillo marked this conversation as resolved.
Show resolved Hide resolved
if (lambdaParameterTypeSyntax is null)
{
TypeSyntax? lambdaParameterTypeSyntax = lambdaParameters[argumentIndex].Type;
continue;
}

// TODO: Don't know if continue or break is the right thing to do here
if (lambdaParameterTypeSyntax is null) continue;
TypeInfo lambdaParameterType = context.SemanticModel.GetTypeInfo(lambdaParameterTypeSyntax, context.CancellationToken);
TypeInfo mockedMethodArgumentType = context.SemanticModel.GetTypeInfo(mockedMethodArguments[argumentIndex].Expression, context.CancellationToken);

TypeInfo lambdaParameterType = context.SemanticModel.GetTypeInfo(lambdaParameterTypeSyntax, context.CancellationToken);
ITypeSymbol? lambdaParameterTypeSymbol = lambdaParameterType.Type;
ITypeSymbol? mockedMethodTypeSymbol = mockedMethodArgumentType.Type;

TypeInfo mockedMethodArgumentType = context.SemanticModel.GetTypeInfo(mockedMethodArguments[argumentIndex].Expression, context.CancellationToken);
if (lambdaParameterTypeSymbol is null || mockedMethodTypeSymbol is null)
{
continue;
}

string? mockedMethodTypeName = mockedMethodArgumentType.ConvertedType?.ToString();
string? lambdaParameterTypeName = lambdaParameterType.ConvertedType?.ToString();
// Check if there's an implicit conversion or identity conversion
Conversion conversion = context.SemanticModel.Compilation.ClassifyConversion(lambdaParameterTypeSymbol, mockedMethodTypeSymbol);

if (!string.Equals(mockedMethodTypeName, lambdaParameterTypeName, StringComparison.Ordinal))
{
Diagnostic diagnostic = callbackLambda.ParameterList.CreateDiagnostic(Rule);
context.ReportDiagnostic(diagnostic);
}
// Where a conversion exists, even if it is explicit
if (conversion.Exists && conversion is not { IsImplicit: false, IsIdentity: false, IsExplicit: false })
MattKotsenas marked this conversation as resolved.
Show resolved Hide resolved
{
continue;
}

Diagnostic diagnostic = lambdaParameters[argumentIndex].GetLocation().CreateDiagnostic(Rule);
context.ReportDiagnostic(diagnostic);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
using Xunit.Abstractions;
using Verifier = Moq.Analyzers.Test.Helpers.CodeFixVerifier<Moq.Analyzers.CallbackSignatureShouldMatchMockedMethodAnalyzer, Moq.Analyzers.CallbackSignatureShouldMatchMockedMethodCodeFix>;

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<object[]> TestData()
{
Expand All @@ -22,7 +30,7 @@ public static IEnumerable<object[]> TestData()
"""new Mock<IFoo>().Setup(x => x.Do(It.IsAny<List<string>>())).Returns((List<string> l) => { return 0; });""",
],
[
"""new Mock<IFoo>().Setup(x => x.Do(It.IsAny<string>())).Callback({|Moq1100:(int i)|} => { });""",
"""new Mock<IFoo>().Setup(x => x.Do(It.IsAny<string>())).Callback(({|Moq1100:int i|}) => { });""",
"""new Mock<IFoo>().Setup(x => x.Do(It.IsAny<string>())).Callback((string s) => { });""",
],
[
Expand All @@ -34,7 +42,7 @@ public static IEnumerable<object[]> TestData()
"""new Mock<IFoo>().Setup(x => x.Do(It.IsAny<int>(), It.IsAny<string>(), It.IsAny<DateTime>())).Callback((int i, string s, DateTime dt) => { });""",
],
[
"""new Mock<IFoo>().Setup(x => x.Do(It.IsAny<List<string>>())).Callback({|Moq1100:(int i)|} => { });""",
"""new Mock<IFoo>().Setup(x => x.Do(It.IsAny<List<string>>())).Callback(({|Moq1100:int i|}) => { });""",
"""new Mock<IFoo>().Setup(x => x.Do(It.IsAny<List<string>>())).Callback((List<string> l) => { });""",
],
[
Expand Down Expand Up @@ -73,6 +81,26 @@ public static IEnumerable<object[]> TestData()
"""new Mock<IFoo>().Setup(x => x.Do(It.IsAny<List<string>>())).Returns(0).Callback((List<string> l) => { });""",
"""new Mock<IFoo>().Setup(x => x.Do(It.IsAny<List<string>>())).Returns(0).Callback((List<string> l) => { });""",
],
[ // Repros for https://github.com/rjmurillo/moq.analyzers/issues/172
"""new Mock<IFoo>().Setup(m => m.Do(It.IsAny<object?>())).Returns((object? bar) => true);""",
"""new Mock<IFoo>().Setup(m => m.Do(It.IsAny<object?>())).Returns((object? bar) => true);""",
],
[
"""new Mock<IFoo>().Setup(m => m.Do(42)).Returns((long bar) => true);""",
"""new Mock<IFoo>().Setup(m => m.Do(42)).Returns((long bar) => true);""",
],
[
"""new Mock<IFoo>().Setup(m => m.Do((long)42)).Returns((long bar) => true);""",
"""new Mock<IFoo>().Setup(m => m.Do((long)42)).Returns((long bar) => true);""",
],
[
"""new Mock<IFoo>().Setup(m => m.Do((long)42)).Returns((long bar) => true);""",
"""new Mock<IFoo>().Setup(m => m.Do((long)42)).Returns((long bar) => true);""",
],
[ // This was also reported as part of 172, but is a different error
"""new Mock<IFoo>().Setup(m => m.Do(42)).Returns((int bar) => true);""",
"""new Mock<IFoo>().Setup(m => m.Do(42)).Returns((int bar) => true);""",
],
}.WithNamespaces().WithMoqReferenceAssemblyGroups();
}

Expand All @@ -91,6 +119,10 @@ internal interface IFoo
int Do(int i, string s, DateTime dt);

int Do(List<string> l);

bool Do(object? bar);

// bool Do(long bar);
rjmurillo marked this conversation as resolved.
Show resolved Hide resolved
}

internal class UnitTest
Expand All @@ -102,6 +134,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);
}
}