Skip to content

Commit

Permalink
Merge pull request #182 from nsubstitute/GH-181-received-in-received-…
Browse files Browse the repository at this point in the history
…in-order-code-fix

GH-181 - code fix provider for NS5001
  • Loading branch information
tpodolak authored Jan 28, 2022
2 parents 638d92a + f24edcb commit f1e6a63
Show file tree
Hide file tree
Showing 10 changed files with 563 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;
using NSubstitute.Analyzers.Shared.CodeFixProviders;

namespace NSubstitute.Analyzers.CSharp.CodeFixProviders;

[ExportCodeFixProvider(LanguageNames.CSharp)]
internal sealed class ReceivedInReceivedInOrderCodeFixProvider : AbstractReceivedInReceivedInOrderCodeFixProvider
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.Operations;
using Document = Microsoft.CodeAnalysis.Document;

namespace NSubstitute.Analyzers.Shared.CodeFixProviders;

internal abstract class AbstractReceivedInReceivedInOrderCodeFixProvider : CodeFixProvider
{
public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;

public override ImmutableArray<string> FixableDiagnosticIds { get; } = ImmutableArray.Create(DiagnosticIdentifiers.ReceivedUsedInReceivedInOrder);

public override Task RegisterCodeFixesAsync(CodeFixContext context)
{
var diagnostic = context.Diagnostics.FirstOrDefault(diag => diag.Descriptor.Id == DiagnosticIdentifiers.ReceivedUsedInReceivedInOrder);
if (diagnostic != null)
{
var codeAction = CodeAction.Create("Remove redundant Received checks", ct => CreateChangedDocument(ct, context, diagnostic), nameof(AbstractReceivedInReceivedInOrderCodeFixProvider));
context.RegisterCodeFix(codeAction, diagnostic);
}

return Task.CompletedTask;
}

private async Task<Document> CreateChangedDocument(CancellationToken cancellationToken, CodeFixContext context, Diagnostic diagnostic)
{
var root = await context.Document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

var invocation = root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true);

var semanticModel = await context.Document.GetSemanticModelAsync(cancellationToken);
if (semanticModel.GetOperation(invocation) is not IInvocationOperation invocationOperation)
{
return context.Document;
}

var syntax = invocationOperation.Instance != null
? invocationOperation.Instance.Syntax
: invocationOperation.Arguments.Single(arg => arg.Parameter.Ordinal == 0).Value.Syntax;

var updatedRoot = root.ReplaceNode(
invocation,
syntax.WithTriviaFrom(invocation));

return context.Document.WithSyntaxRoot(updatedRoot);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;
using NSubstitute.Analyzers.Shared.CodeFixProviders;

namespace NSubstitute.Analyzers.VisualBasic.CodeFixProviders;

[ExportCodeFixProvider(LanguageNames.VisualBasic)]
internal sealed class ReceivedInReceivedInOrderCodeFixProvider : AbstractReceivedInReceivedInOrderCodeFixProvider
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
using System.Threading.Tasks;
using Xunit;

namespace NSubstitute.Analyzers.Tests.CSharp.CodeFixProviderTests.ReceivedInReceivedInOrderCodeFixProviderTests;

public class ReceivedAsExtensionMethodTests : ReceivedInReceivedInOrderCodeFixVerifier
{
[Fact]
public override async Task RemovesReceivedChecks_WhenReceivedChecksHasNoArguments()
{
var oldSource = @"using NSubstitute;
using NSubstitute.ReceivedExtensions;
namespace MyNamespace
{
public interface IFoo
{
int Bar();
int Bar(int x);
}
public class FooTests
{
public void Test()
{
var substitute = NSubstitute.Substitute.For<IFoo>();
Received.InOrder(() =>
{
substitute.Received().Bar();
substitute.Received().Bar(Arg.Any<int>());
substitute.ReceivedWithAnyArgs().Bar(Arg.Any<int>());
});
}
}
}";

var newSource = @"using NSubstitute;
using NSubstitute.ReceivedExtensions;
namespace MyNamespace
{
public interface IFoo
{
int Bar();
int Bar(int x);
}
public class FooTests
{
public void Test()
{
var substitute = NSubstitute.Substitute.For<IFoo>();
Received.InOrder(() =>
{
substitute.Bar();
substitute.Bar(Arg.Any<int>());
substitute.Bar(Arg.Any<int>());
});
}
}
}";
await VerifyFix(oldSource, newSource);
}

[Fact]
public override async Task RemovesReceivedChecks_WhenReceivedChecksHasArguments()
{
var oldSource = @"using NSubstitute;
using NSubstitute.ReceivedExtensions;
namespace MyNamespace
{
public interface IFoo
{
int Bar();
int Bar(int x);
}
public class FooTests
{
public void Test()
{
var substitute = NSubstitute.Substitute.For<IFoo>();
Received.InOrder(() =>
{
substitute.Received(1).Bar();
substitute.Received(1).Bar(Arg.Any<int>());
});
}
}
}";

var newSource = @"using NSubstitute;
using NSubstitute.ReceivedExtensions;
namespace MyNamespace
{
public interface IFoo
{
int Bar();
int Bar(int x);
}
public class FooTests
{
public void Test()
{
var substitute = NSubstitute.Substitute.For<IFoo>();
Received.InOrder(() =>
{
substitute.Bar();
substitute.Bar(Arg.Any<int>());
});
}
}
}";
await VerifyFix(oldSource, newSource);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
using System.Threading.Tasks;
using Xunit;

namespace NSubstitute.Analyzers.Tests.CSharp.CodeFixProviderTests.ReceivedInReceivedInOrderCodeFixProviderTests;

public class ReceivedAsOrdinaryMethodTests : ReceivedInReceivedInOrderCodeFixVerifier
{
[Fact]
public override async Task RemovesReceivedChecks_WhenReceivedChecksHasNoArguments()
{
var oldSource = @"using NSubstitute;
using NSubstitute.ReceivedExtensions;
namespace MyNamespace
{
public interface IFoo
{
int Bar();
int Bar(int x);
}
public class FooTests
{
public void Test()
{
var substitute = NSubstitute.Substitute.For<IFoo>();
Received.InOrder(() =>
{
SubstituteExtensions.Received(substitute).Bar();
SubstituteExtensions.Received(substitute).Bar(Arg.Any<int>());
SubstituteExtensions.ReceivedWithAnyArgs(substitute).Bar(Arg.Any<int>());
});
}
}
}";

var newSource = @"using NSubstitute;
using NSubstitute.ReceivedExtensions;
namespace MyNamespace
{
public interface IFoo
{
int Bar();
int Bar(int x);
}
public class FooTests
{
public void Test()
{
var substitute = NSubstitute.Substitute.For<IFoo>();
Received.InOrder(() =>
{
substitute.Bar();
substitute.Bar(Arg.Any<int>());
substitute.Bar(Arg.Any<int>());
});
}
}
}";
await VerifyFix(oldSource, newSource);
}

[Fact]
public override async Task RemovesReceivedChecks_WhenReceivedChecksHasArguments()
{
var oldSource = @"using NSubstitute;
using NSubstitute.ReceivedExtensions;
namespace MyNamespace
{
public interface IFoo
{
int Bar();
int Bar(int x);
}
public class FooTests
{
public void Test()
{
var substitute = NSubstitute.Substitute.For<IFoo>();
Received.InOrder(() =>
{
SubstituteExtensions.Received(substitute, 1).Bar();
SubstituteExtensions.Received(substitute, 1).Bar(Arg.Any<int>());
});
}
}
}";

var newSource = @"using NSubstitute;
using NSubstitute.ReceivedExtensions;
namespace MyNamespace
{
public interface IFoo
{
int Bar();
int Bar(int x);
}
public class FooTests
{
public void Test()
{
var substitute = NSubstitute.Substitute.For<IFoo>();
Received.InOrder(() =>
{
substitute.Bar();
substitute.Bar(Arg.Any<int>());
});
}
}
}";
await VerifyFix(oldSource, newSource);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.Diagnostics;
using NSubstitute.Analyzers.CSharp.CodeFixProviders;
using NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers;
using NSubstitute.Analyzers.Shared.CodeFixProviders;
using NSubstitute.Analyzers.Tests.Shared.CodeFixProviders;

namespace NSubstitute.Analyzers.Tests.CSharp.CodeFixProviderTests.ReceivedInReceivedInOrderCodeFixProviderTests;

public abstract class ReceivedInReceivedInOrderCodeFixVerifier : CSharpCodeFixVerifier, IReceivedInReceivedInOrderCodeFixVerifier
{
protected override DiagnosticAnalyzer DiagnosticAnalyzer { get; } = new ReceivedInReceivedInOrderAnalyzer();

protected override CodeFixProvider CodeFixProvider { get; } = new ReceivedInReceivedInOrderCodeFixProvider();

public abstract Task RemovesReceivedChecks_WhenReceivedChecksHasNoArguments();

public abstract Task RemovesReceivedChecks_WhenReceivedChecksHasArguments();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System.Threading.Tasks;

namespace NSubstitute.Analyzers.Tests.Shared.CodeFixProviders;

public interface IReceivedInReceivedInOrderCodeFixVerifier
{
Task RemovesReceivedChecks_WhenReceivedChecksHasNoArguments();

Task RemovesReceivedChecks_WhenReceivedChecksHasArguments();
}
Loading

0 comments on commit f1e6a63

Please sign in to comment.