diff --git a/src/nunit.analyzers.tests/EqualToIncompatibleTypes/EqualToIncompatibleTypesAnalyzerTests.cs b/src/nunit.analyzers.tests/EqualToIncompatibleTypes/EqualToIncompatibleTypesAnalyzerTests.cs index aeebfcd4..5b01fcd0 100644 --- a/src/nunit.analyzers.tests/EqualToIncompatibleTypes/EqualToIncompatibleTypesAnalyzerTests.cs +++ b/src/nunit.analyzers.tests/EqualToIncompatibleTypes/EqualToIncompatibleTypesAnalyzerTests.cs @@ -951,5 +951,34 @@ public void AnalyzeWhenThrowsWithTypeConstraintIsUsedWithDifferentType(string co RoslynAssert.Diagnostics(analyzer, testCode); } + + [Test] + public void AnalyzeWhenComparingUriWithString() + { + // Uri.Equals(object? comparand) has code that checks if comparand is a string. + // There is no separate overload for Uri.Equals(string?) to check for. + var testCode = TestUtility.WrapInTestMethod(@" + const string testString = ""test""; + Uri testUri = new Uri(""test"", UriKind.Relative); + + Assert.That(testUri, Is.EqualTo(testString)); + "); + + RoslynAssert.Valid(analyzer, testCode); + } + + [Test] + public void AnalyzeWhenComparingStringWithUri() + { + // string.Equals(Uri) always fails + var testCode = TestUtility.WrapInTestMethod(@" + const string testString = ""test""; + Uri testUri = new Uri(""test"", UriKind.Relative); + + Assert.That(testString, Is.EqualTo(↓testUri)); + "); + + RoslynAssert.Diagnostics(analyzer, testCode); + } } } diff --git a/src/nunit.analyzers.tests/Helpers/NUnitEqualityComparerHelperTests.cs b/src/nunit.analyzers.tests/Helpers/NUnitEqualityComparerHelperTests.cs index 77094090..e54384eb 100644 --- a/src/nunit.analyzers.tests/Helpers/NUnitEqualityComparerHelperTests.cs +++ b/src/nunit.analyzers.tests/Helpers/NUnitEqualityComparerHelperTests.cs @@ -213,6 +213,21 @@ public void TrueForArrayWithElementErrorType() AssertThatCommutativeEqual(leftTypeSymbol, rightTypeSymbol, compilation, Is.True); } + [Test] + public void UriCanEqualStringButStringCanNotEqualUri() + { + var compilation = TestHelpers.CreateCompilation(); + + var stringSymbol = compilation.GetSpecialType(SpecialType.System_String); + var uriSymbol = compilation.GetTypeByMetadataName("System.Uri"); + + Assert.Multiple(() => + { + Assert.That(NUnitEqualityComparerHelper.CanBeEqual(uriSymbol, stringSymbol, compilation), Is.True); + Assert.That(NUnitEqualityComparerHelper.CanBeEqual(stringSymbol, uriSymbol, compilation), Is.False); + }); + } + private static void AssertThatCommutativeEqual(ITypeSymbol? leftTypeSymbol, ITypeSymbol? rightTypeSymbol, Compilation compilation, Constraint constraint) { Assert.Multiple(() => diff --git a/src/nunit.analyzers/Helpers/NUnitEqualityComparerHelper.cs b/src/nunit.analyzers/Helpers/NUnitEqualityComparerHelper.cs index 3b2def6e..1d239556 100644 --- a/src/nunit.analyzers/Helpers/NUnitEqualityComparerHelper.cs +++ b/src/nunit.analyzers/Helpers/NUnitEqualityComparerHelper.cs @@ -100,6 +100,10 @@ public static bool CanBeEqual( return CanBeEqual(actualKeyType, expectedKeyType, compilation, checkedTypes) && CanBeEqual(actualValueType, expectedValueType, compilation, checkedTypes); } + + // Uri.Equals(string) works, but not string.Equals(Uri) + if (IsUri(actualFullName) && expectedType.SpecialType == SpecialType.System_String) + return true; } // IEnumerables @@ -182,6 +186,11 @@ private static bool IsTuple(string fullName) return fullName.StartsWith("System.Tuple`", StringComparison.Ordinal); } + private static bool IsUri(string fullName) + { + return fullName.Equals("System.Uri", StringComparison.Ordinal); + } + private static bool IsIEquatable(ITypeSymbol typeSymbol, ITypeSymbol equatableTypeArguments) { return typeSymbol.AllInterfaces.Any(i => i.TypeArguments.Length == 1