Skip to content

Commit

Permalink
NUnit2010: Exclusions
Browse files Browse the repository at this point in the history
We cannot convert `a.Equals(b)` into `a, Is.EqualTo(b)`
if a doesn't implement `IEquatable<b>`
  • Loading branch information
manfred-brands committed Apr 14, 2024
1 parent d14cc57 commit 52bb832
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -305,5 +305,108 @@ public void TestMethod()

RoslynAssert.Valid(analyzer, testCode, Settings.Default.WithMetadataReferences(metadataReferences));
}

[Test]
public void EqualsMethodButClassDoesNotImplementIEqualityInterface()
{
var testCode = TestUtility.WrapClassInNamespaceAndAddUsing(@"
[TestFixture]
public class TestClass
{
[Test]
public void TestMethod()
{
Foo expected = new Foo();
Foo actual = new Foo();
Assert.That(actual.Equals(expected), Is.True);
}
private sealed class Foo
{
public bool Equals(Foo other) => true;
}
}");

RoslynAssert.Valid(analyzer, testCode);
}

[Test]
public void EqualsMethodAndClassDoesImplementIEqualityInterface()
{
var testCode = TestUtility.WrapClassInNamespaceAndAddUsing(@"
[TestFixture]
public class TestClass
{
[Test]
public void TestMethod()
{
Foo expected = new Foo();
Foo actual = new Foo();
Assert.That(actual.Equals(expected), Is.True);
}
private sealed class Foo : IEquatable<Foo>
{
public bool Equals(Foo? other) => true;
}
}");

RoslynAssert.Diagnostics(analyzer, isEqualToDiagnostic, testCode);
}

[Test]
public void EqualsMethodOnObject()
{
var testCode = TestUtility.WrapClassInNamespaceAndAddUsing(@"
[TestFixture]
public class TestClass
{
[Test]
public void TestMethod()
{
object expected = new Foo();
Foo actual = new Foo();
Assert.That(actual.Equals(expected), Is.True);
}
private sealed class Foo : IEquatable<Foo>
{
public bool Equals(Foo? other) => true;
}
}");

RoslynAssert.Diagnostics(analyzer, isEqualToDiagnostic, testCode);
}

[Test]
public void EqualsMethodButClassDoesNotImplementIEqualityInterfaceForOtherClass()
{
var testCode = TestUtility.WrapClassInNamespaceAndAddUsing(@"
[TestFixture]
public class TestClass
{
[Test]
public void FooIsMyFriend()
{
Friend expected = new Friend();
Foo actual = new Foo();
Assert.That(actual.Equals(expected), Is.True);
}
private sealed class Foo : IEquatable<Foo>
{
public bool Equals(Foo? other) => true;
public bool Equals(Friend? other) => true;
}
private sealed class Friend : IEquatable<Friend>
{
public bool Equals(Friend? other) => true;
public bool Equals(Foo? other) => true;
}
}");

RoslynAssert.Valid(analyzer, testCode);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,35 @@ private static bool IsInstanceObjectEquals(IOperation operation)

var methodSymbol = invocation.TargetMethod;

return methodSymbol is not null
if (methodSymbol is not null
&& !methodSymbol.IsStatic
&& methodSymbol.Parameters.Length == 1
&& methodSymbol.Name == nameof(object.Equals)
&& invocation.Arguments.Length == 1
&& !IsRefStruct(invocation.Arguments[0])
&& invocation.Instance is not null
&& !IsRefStruct(invocation.Instance);
&& !IsRefStruct(invocation.Instance))
{
// Not all Equals qualify
if (methodSymbol.Parameters[0].Type.SpecialType == SpecialType.System_Object)
{
// object.Equals(object)
return true;
}

foreach (var implementedInterface in methodSymbol.ContainingType.Interfaces)
{
if (implementedInterface.TypeArguments.Length == 1
&& implementedInterface.Name == "IEquatable"
&& SymbolEqualityComparer.Default.Equals(implementedInterface.TypeArguments[0], invocation.Arguments[0].Parameter?.Type))
{
// IEquality<T>.Equals(T)
return true;
}
}
}

return false;
}
}
}

0 comments on commit 52bb832

Please sign in to comment.