File: FindSymbols\FindReferences\MetadataUnifyingEquivalenceComparer.cs
Web Access
Project: src\src\Workspaces\Core\Portable\Microsoft.CodeAnalysis.Workspaces.csproj (Microsoft.CodeAnalysis.Workspaces)
// 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 System.Linq;
using Microsoft.CodeAnalysis.Shared.Utilities;
 
namespace Microsoft.CodeAnalysis.FindSymbols;
 
internal sealed class MetadataUnifyingEquivalenceComparer : IEqualityComparer<ISymbol>
{
    public static readonly IEqualityComparer<ISymbol> Instance = new MetadataUnifyingEquivalenceComparer();
 
    private MetadataUnifyingEquivalenceComparer()
    {
    }
 
    public bool Equals(ISymbol? x, ISymbol? y)
    {
        // If either symbol is from source, then we must do stricter equality. Consider this:
        //
        //     S1 <-> M <-> S2     (where S# = source symbol, M = some metadata symbol)
        //
        // In this case, imagine that both the comparisons denoted by <-> were done with the
        // SymbolEquivalenceComparer, and returned true. If S1 and S2 were from different projects,
        // they would compare false but transitivity would say they must be true. Another way to think
        // of this is any use of a source symbol "poisons" the comparison and requires it to be stricter.
        if (x == null || y == null || IsInSource(x) || IsInSource(y))
        {
            return object.Equals(x, y);
        }
 
        // Both of the symbols are from metadata, so defer to the equivalence comparer
        return SymbolEquivalenceComparer.Instance.Equals(x, y);
    }
 
    public int GetHashCode(ISymbol obj)
    {
        if (IsInSource(obj))
        {
            return obj.GetHashCode();
        }
        else
        {
            return SymbolEquivalenceComparer.Instance.GetHashCode(obj);
        }
    }
 
    private static bool IsInSource(ISymbol symbol)
        => symbol.Locations.Any(static l => l.IsInSource);
}