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
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
using Verifier = TUnit.Assertions.Analyzers.CodeFixers.Tests.Verifiers.CSharpCodeFixVerifier<
TUnit.Assertions.Analyzers.CollectionIsEqualToAnalyzer,
TUnit.Assertions.Analyzers.CodeFixers.CollectionIsEqualToCodeFixProvider>;

namespace TUnit.Assertions.Analyzers.CodeFixers.Tests;

public class CollectionIsEqualToCodeFixProviderTests
{
[Test]
public async Task Rewrites_IsEqualTo_To_IsEquivalentTo()
{
await Verifier
.VerifyCodeFixAsync(
"""
using System.Collections.Generic;
using System.Threading.Tasks;
using TUnit.Assertions;
using TUnit.Assertions.Extensions;
using TUnit.Core;

public class MyClass
{
[Test]
public async Task Test()
{
var a = new List<int> { 1, 2 };
var b = new List<int> { 1, 2 };
await Assert.That(a).{|#0:IsEqualTo(b)|};
}
}
""",
Verifier.Diagnostic(Rules.CollectionIsEqualToUsesReferenceEquality)
.WithLocation(0),
"""
using System.Collections.Generic;
using System.Threading.Tasks;
using TUnit.Assertions;
using TUnit.Assertions.Extensions;
using TUnit.Core;

public class MyClass
{
[Test]
public async Task Test()
{
var a = new List<int> { 1, 2 };
var b = new List<int> { 1, 2 };
await Assert.That(a).IsEquivalentTo(b);
}
}
"""
);
}

[Test]
public async Task Fix_Preserves_Chained_Calls()
{
await Verifier.VerifyCodeFixAsync(
"""
using System.Collections.Generic;
using System.Threading.Tasks;
using TUnit.Assertions;
using TUnit.Assertions.Extensions;
using TUnit.Core;

public class MyClass
{
[Test]
public async Task Test()
{
var a = new List<int> { 1, 2 };
var b = new List<int> { 1, 2 };
await Assert.That(a).{|#0:IsEqualTo(b)|}.And.IsNotNull();
}
}
""",
Verifier.Diagnostic(Rules.CollectionIsEqualToUsesReferenceEquality).WithLocation(0),
"""
using System.Collections.Generic;
using System.Threading.Tasks;
using TUnit.Assertions;
using TUnit.Assertions.Extensions;
using TUnit.Core;

public class MyClass
{
[Test]
public async Task Test()
{
var a = new List<int> { 1, 2 };
var b = new List<int> { 1, 2 };
await Assert.That(a).IsEquivalentTo(b).And.IsNotNull();
}
}
""");
}

[Test]
public async Task Fix_Works_On_Arrays()
{
await Verifier.VerifyCodeFixAsync(
"""
using System.Threading.Tasks;
using TUnit.Assertions;
using TUnit.Assertions.Extensions;
using TUnit.Core;

public class MyClass
{
[Test]
public async Task Test()
{
int[] a = { 1 };
int[] b = { 1 };
await Assert.That(a).{|#0:IsEqualTo(b)|};
}
}
""",
Verifier.Diagnostic(Rules.CollectionIsEqualToUsesReferenceEquality).WithLocation(0),
"""
using System.Threading.Tasks;
using TUnit.Assertions;
using TUnit.Assertions.Extensions;
using TUnit.Core;

public class MyClass
{
[Test]
public async Task Test()
{
int[] a = { 1 };
int[] b = { 1 };
await Assert.That(a).IsEquivalentTo(b);
}
}
""");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using System.Collections.Immutable;
using System.Composition;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace TUnit.Assertions.Analyzers.CodeFixers;

[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(CollectionIsEqualToCodeFixProvider)), Shared]
public class CollectionIsEqualToCodeFixProvider : CodeFixProvider
{
public sealed override ImmutableArray<string> FixableDiagnosticIds { get; } =
ImmutableArray.Create(Rules.CollectionIsEqualToUsesReferenceEquality.Id);

public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;

public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
if (root is null)
{
return;
}

foreach (var diagnostic in context.Diagnostics)
{
// Analyzer reports a span covering `IsEqualTo(...)`; FindNode returns the enclosing invocation.
if (root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true)
is not InvocationExpressionSyntax { Expression: MemberAccessExpressionSyntax { Name: IdentifierNameSyntax identifier } })
{
continue;
}

context.RegisterCodeFix(
CodeAction.Create(
title: Resources.TUnitAssertions0016CodeFixTitle,
createChangedDocument: c => ReplaceAsync(context.Document, identifier, c),
equivalenceKey: nameof(Resources.TUnitAssertions0016CodeFixTitle)),
diagnostic);
}
}

private static async Task<Document> ReplaceAsync(Document document, IdentifierNameSyntax identifier, CancellationToken cancellationToken)
{
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
if (root is null)
{
return document;
}

var replacement = SyntaxFactory
.IdentifierName("IsEquivalentTo")
.WithTriviaFrom(identifier);

return document.WithSyntaxRoot(root.ReplaceNode(identifier, replacement));
}
}
Loading
Loading