diff --git a/src/Compilers/Core/Portable/InternalUtilities/EmptyComparer.cs b/src/Compilers/Core/Portable/InternalUtilities/EmptyComparer.cs deleted file mode 100644 index 0f61efb45d893..0000000000000 --- a/src/Compilers/Core/Portable/InternalUtilities/EmptyComparer.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Generic; -using Microsoft.CodeAnalysis; - -namespace Roslyn.Utilities -{ - /// - /// Very cheap trivial comparer that never matches the keys, - /// should only be used in empty dictionaries. - /// - internal sealed class EmptyComparer : IEqualityComparer - { - public static readonly EmptyComparer Instance = new EmptyComparer(); - - private EmptyComparer() - { - } - - bool IEqualityComparer.Equals(object? a, object? b) - => throw ExceptionUtilities.Unreachable(); - - int IEqualityComparer.GetHashCode(object s) - { - // dictionary will call this often - return 0; - } - } -} diff --git a/src/Dependencies/Contracts/EqualityComparerExtensions.cs b/src/Dependencies/Contracts/EqualityComparerExtensions.cs new file mode 100644 index 0000000000000..e67f8320a7aaf --- /dev/null +++ b/src/Dependencies/Contracts/EqualityComparerExtensions.cs @@ -0,0 +1,63 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#nullable enable + +#if !NET8_0_OR_GREATER +using System.Diagnostics.CodeAnalysis; +#endif + +namespace System.Collections.Generic; + +internal static class RoslynEqualityComparerExtensions +{ +#if NET8_0_OR_GREATER + + // for binary compatibility + public static EqualityComparer Create(Func equals, Func? getHashCode = null) + => EqualityComparer.Create(equals, getHashCode); + +#else + // Implementation based on https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.cs + + extension(EqualityComparer) + { + /// + /// Creates an by using the specified delegates as the implementation of the comparer's + /// and methods. + /// + /// The delegate to use to implement the method. + /// + /// The delegate to use to implement the method. + /// If no delegate is supplied, calls to the resulting comparer's + /// will throw . + /// + /// The new comparer. + public static EqualityComparer Create(Func equals, Func? getHashCode = null) + { + getHashCode ??= _ => throw new NotSupportedException(); + return new DelegateEqualityComparer(equals, getHashCode); + } + } + + private sealed class DelegateEqualityComparer(Func equals, Func getHashCode) : EqualityComparer + { + private readonly Func _equals = equals; + private readonly Func _getHashCode = getHashCode; + + public override bool Equals(T? x, T? y) => + _equals(x, y); + + public override int GetHashCode(T obj) => + _getHashCode(obj); + + public override bool Equals(object? obj) + => obj is DelegateEqualityComparer other && _equals == other._equals && _getHashCode == other._getHashCode; + + public override int GetHashCode() + => unchecked(_equals.GetHashCode() * (int)0xA5555529 + _getHashCode.GetHashCode()); + } +#endif +} + diff --git a/src/Dependencies/Contracts/Microsoft.CodeAnalysis.Contracts.projitems b/src/Dependencies/Contracts/Microsoft.CodeAnalysis.Contracts.projitems index e3725f02a07f8..504f3c43ac0b3 100644 --- a/src/Dependencies/Contracts/Microsoft.CodeAnalysis.Contracts.projitems +++ b/src/Dependencies/Contracts/Microsoft.CodeAnalysis.Contracts.projitems @@ -25,6 +25,8 @@ + + diff --git a/src/Compilers/Core/Portable/InternalUtilities/ReferenceEqualityComparer.cs b/src/Dependencies/Contracts/ReferenceEqualityComparer.cs similarity index 99% rename from src/Compilers/Core/Portable/InternalUtilities/ReferenceEqualityComparer.cs rename to src/Dependencies/Contracts/ReferenceEqualityComparer.cs index 8176987e9e9cd..ce160297459c0 100644 --- a/src/Compilers/Core/Portable/InternalUtilities/ReferenceEqualityComparer.cs +++ b/src/Dependencies/Contracts/ReferenceEqualityComparer.cs @@ -4,7 +4,9 @@ using System.Runtime.CompilerServices; -#if NET5_0_OR_GREATER +#nullable enable + +#if NET #pragma warning disable RS0016 // Add public types and members to the declared API (this is a supporting forwarder for an internal polyfill API) [assembly: TypeForwardedTo(typeof(System.Collections.Generic.ReferenceEqualityComparer))] diff --git a/src/EditorFeatures/Test/Collections/Immutable/Maps/MapTests.cs b/src/EditorFeatures/Test/Collections/Immutable/Maps/MapTests.cs index 1908f55551332..c7e93e8b55e96 100644 --- a/src/EditorFeatures/Test/Collections/Immutable/Maps/MapTests.cs +++ b/src/EditorFeatures/Test/Collections/Immutable/Maps/MapTests.cs @@ -82,7 +82,9 @@ public void TestRemove() [Fact] public void TestPathology() { - var map = ImmutableDictionary.Create(new PathologicalComparer()) + var comparer = EqualityComparer.Create(EqualityComparer.Default.Equals, _ => 0); + + var map = ImmutableDictionary.Create(comparer) .Add("1", 1) .Add("2", 2) .Add("3", 3) @@ -118,13 +120,4 @@ public void TestPathology() map = map.Remove("5"); Assert.Empty(map); } - - private sealed class PathologicalComparer : IEqualityComparer - { - public bool Equals(T x, T y) - => EqualityComparer.Default.Equals(x, y); - - public int GetHashCode(T obj) - => 0; - } } diff --git a/src/Features/Core/Portable/Completion/Providers/AbstractKeywordCompletionProvider.cs b/src/Features/Core/Portable/Completion/Providers/AbstractKeywordCompletionProvider.cs index 84fe32c5f955d..2c44eb5daa37e 100644 --- a/src/Features/Core/Portable/Completion/Providers/AbstractKeywordCompletionProvider.cs +++ b/src/Features/Core/Portable/Completion/Providers/AbstractKeywordCompletionProvider.cs @@ -19,7 +19,9 @@ namespace Microsoft.CodeAnalysis.Completion.Providers; internal abstract partial class AbstractKeywordCompletionProvider : LSPCompletionProvider where TContext : SyntaxContext { - private static readonly Comparer s_comparer = new(); + private static readonly EqualityComparer s_comparer = EqualityComparer.Create( + static (x, y) => x?.DisplayText == y?.DisplayText, + static x => x.DisplayText.GetHashCode()); private readonly ImmutableArray> _keywordRecommenders; @@ -72,13 +74,4 @@ private async Task> RecommendKeywordsAsync( public sealed override Task GetTextChangeAsync(Document document, CompletionItem item, char? ch, CancellationToken cancellationToken) => Task.FromResult((TextChange?)new TextChange(item.Span, item.DisplayText)); - - private sealed class Comparer : IEqualityComparer - { - public bool Equals(CompletionItem? x, CompletionItem? y) - => x?.DisplayText == y?.DisplayText; - - public int GetHashCode(CompletionItem obj) - => Hash.Combine(obj.DisplayText.GetHashCode(), obj.DisplayText.GetHashCode()); - } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems index 6f9309d886cb3..755a43b260eb3 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems @@ -1,4 +1,4 @@ - + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) @@ -19,7 +19,6 @@ -