Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
44 changes: 29 additions & 15 deletions src/Moq.Analyzers/SetupShouldNotIncludeAsyncResultAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,24 +32,38 @@ public override void Initialize(AnalysisContext context)
[System.Diagnostics.CodeAnalysis.SuppressMessage("Maintainability", "AV1500:Member or local function contains too many statements", Justification = "Tracked in https://github.com/rjmurillo/moq.analyzers/issues/90")]
private static void Analyze(SyntaxNodeAnalysisContext context)
{
// Check Moq version and skip analysis if the version is 4.16.0 or later
AssemblyIdentity? moqAssembly = context.Compilation.ReferencedAssemblyNames.FirstOrDefault(a => a.Name.Equals("Moq", StringComparison.OrdinalIgnoreCase));

if (moqAssembly != null && moqAssembly.Version >= new Version(4, 16, 0))
{
// Skip analysis for Moq 4.16.0 or later
return;
}

Comment thread
rjmurillo marked this conversation as resolved.
InvocationExpressionSyntax setupInvocation = (InvocationExpressionSyntax)context.Node;

if (setupInvocation.Expression is MemberAccessExpressionSyntax memberAccessExpression && context.SemanticModel.IsMoqSetupMethod(memberAccessExpression, context.CancellationToken))
if (setupInvocation.Expression is not MemberAccessExpressionSyntax memberAccessExpression ||
!context.SemanticModel.IsMoqSetupMethod(memberAccessExpression, context.CancellationToken))
{
ExpressionSyntax? mockedMemberExpression = setupInvocation.FindMockedMemberExpressionFromSetupMethod();
if (mockedMemberExpression == null)
{
return;
}

SymbolInfo symbolInfo = context.SemanticModel.GetSymbolInfo(mockedMemberExpression, context.CancellationToken);
if ((symbolInfo.Symbol is IPropertySymbol || symbolInfo.Symbol is IMethodSymbol)
&& !symbolInfo.Symbol.IsOverridable()
&& symbolInfo.Symbol.IsMethodReturnTypeTask())
{
Diagnostic diagnostic = mockedMemberExpression.GetLocation().CreateDiagnostic(Rule);
context.ReportDiagnostic(diagnostic);
}
return;
}

ExpressionSyntax? mockedMemberExpression = setupInvocation.FindMockedMemberExpressionFromSetupMethod();
if (mockedMemberExpression == null)
{
return;
}

SymbolInfo symbolInfo = context.SemanticModel.GetSymbolInfo(mockedMemberExpression, context.CancellationToken);
if (symbolInfo.Symbol is not (IPropertySymbol or IMethodSymbol)
|| symbolInfo.Symbol.IsOverridable()
|| !symbolInfo.Symbol.IsMethodReturnTypeTask())
{
return;
}

Diagnostic diagnostic = mockedMemberExpression.GetLocation().CreateDiagnostic(Rule);
context.ReportDiagnostic(diagnostic);
Comment thread
rjmurillo marked this conversation as resolved.
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using Xunit.Abstractions;
using Verifier = Moq.Analyzers.Test.Helpers.CodeFixVerifier<Moq.Analyzers.CallbackSignatureShouldMatchMockedMethodAnalyzer, Moq.CodeFixes.CallbackSignatureShouldMatchMockedMethodCodeFix>;

namespace Moq.Analyzers.Test;
Expand Down
1 change: 1 addition & 0 deletions tests/Moq.Analyzers.Test/GlobalUsings.cs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
global using Moq.Analyzers.Test.Helpers;
global using Xunit.Abstractions;
Comment thread
rjmurillo marked this conversation as resolved.
8 changes: 8 additions & 0 deletions tests/Moq.Analyzers.Test/Helpers/TestDataExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ public static IEnumerable<object[]> WithMoqReferenceAssemblyGroups(this IEnumera
}
}

public static IEnumerable<object[]> WithOldMoqReferenceAssemblyGroups(this IEnumerable<object[]> data)
{
foreach (object[] item in data)
{
yield return item.Prepend(ReferenceAssemblyCatalog.Net80WithOldMoq).ToArray();
}
}
Comment thread
rjmurillo marked this conversation as resolved.

public static IEnumerable<object[]> WithNewMoqReferenceAssemblyGroups(this IEnumerable<object[]> data)
{
foreach (object[] item in data)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using Xunit.Abstractions;
using Verifier = Moq.Analyzers.Test.Helpers.AnalyzerVerifier<Moq.Analyzers.SetupShouldBeUsedOnlyForOverridableMembersAnalyzer>;

namespace Moq.Analyzers.Test;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,41 +2,50 @@

namespace Moq.Analyzers.Test;

public class SetupShouldNotIncludeAsyncResultAnalyzerTests
public class SetupShouldNotIncludeAsyncResultAnalyzerTests(ITestOutputHelper output)
{
public static IEnumerable<object[]> TestData()
{
return new object[][]
{
["""new Mock<AsyncClient>().Setup(c => c.TaskAsync());"""],

// Prior to Moq 4.16, the setup helper ReturnsAsync(), ThrowsAsync() are preferred
["""new Mock<AsyncClient>().Setup(c => c.GenericTaskAsync()).ReturnsAsync(string.Empty);"""],

// Starting with Moq 4.16, you can simply .Setup the returned tasks's .Result property
["""new Mock<AsyncClient>().Setup(c => {|Moq1201:c.GenericTaskAsync().Result|});"""],
}.WithNamespaces().WithMoqReferenceAssemblyGroups();
}.WithNamespaces().WithOldMoqReferenceAssemblyGroups();
Comment thread
rjmurillo marked this conversation as resolved.
}

[Theory]
[MemberData(nameof(TestData))]
public async Task ShouldAnalyzeSetupForAsyncResult(string referenceAssemblyGroup, string @namespace, string mock)
{
string source =
$$"""
{{@namespace}}

public class AsyncClient
{
public virtual Task TaskAsync() => Task.CompletedTask;

public virtual Task<string> GenericTaskAsync() => Task.FromResult(string.Empty);
}

internal class UnitTest
{
private void Test()
{
{{mock}}
}
}
""";

output.WriteLine(source);

await Verifier.VerifyAnalyzerAsync(
$$"""
{{@namespace}}

public class AsyncClient
{
public virtual Task TaskAsync() => Task.CompletedTask;

public virtual Task<string> GenericTaskAsync() => Task.FromResult(string.Empty);
}

internal class UnitTest
{
private void Test()
{
{{mock}}
}
}
""",
source,
referenceAssemblyGroup);
}
}