File: src\Workspaces\SharedUtilitiesAndExtensions\Compiler\CSharp\Utilities\NameSyntaxComparer.cs
Web Access
Project: src\src\Workspaces\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.Workspaces.csproj (Microsoft.CodeAnalysis.CSharp.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 Microsoft.CodeAnalysis.CSharp.Syntax;
 
namespace Microsoft.CodeAnalysis.CSharp.Utilities;
 
internal class NameSyntaxComparer : IComparer<NameSyntax?>
{
    private readonly IComparer<SyntaxToken> _tokenComparer;
    internal readonly TypeSyntaxComparer TypeComparer;
 
    internal NameSyntaxComparer(IComparer<SyntaxToken> tokenComparer)
    {
        _tokenComparer = tokenComparer;
        TypeComparer = new TypeSyntaxComparer(tokenComparer, this);
    }
 
    public static IComparer<NameSyntax?> Create()
        => Create(TokenComparer.NormalInstance);
 
    public static IComparer<NameSyntax?> Create(IComparer<SyntaxToken> tokenComparer)
        => new NameSyntaxComparer(tokenComparer);
 
    public int Compare(NameSyntax? x, NameSyntax? y)
    {
        if (x == y)
            return 0;
 
        return (x, y) switch
        {
            (null, null) => 0,
            (null, _) => -1,
            (_, null) => 1,
            ({ IsMissing: true }, { IsMissing: true }) => 0,
            ({ IsMissing: true }, _) => -1,
            (_, { IsMissing: true }) => 1,
            (IdentifierNameSyntax identifierX, IdentifierNameSyntax identifierY) => _tokenComparer.Compare(identifierX.Identifier, identifierY.Identifier),
            (GenericNameSyntax genericX, GenericNameSyntax genericY) => Compare(genericX, genericY),
            (IdentifierNameSyntax identifierX, GenericNameSyntax genericY) =>
                _tokenComparer.Compare(identifierX.Identifier, genericY.Identifier) is var diff && diff != 0
                    ? diff
                    : -1, // Goo goes before Goo<T>
            (GenericNameSyntax genericX, IdentifierNameSyntax identifierY) =>
                _tokenComparer.Compare(genericX.Identifier, identifierY.Identifier) is var diff && diff != 0
                    ? diff
                    : -1, // Goo<T> goes after Goo
            (_, _) => DecomposeCompare(x, y),
        };
 
        int DecomposeCompare(NameSyntax x, NameSyntax y)
        {
            // At this point one or both of the nodes is a dotted name or
            // aliased name.  Break them apart into individual pieces and
            // compare those.
 
            var xNameParts = DecomposeNameParts(x);
            var yNameParts = DecomposeNameParts(y);
 
            for (var i = 0; i < xNameParts.Count && i < yNameParts.Count; i++)
            {
                var compare = Compare(xNameParts[i], yNameParts[i]);
                if (compare != 0)
                    return compare;
            }
 
            // they matched up to this point.  The shorter one should come
            // first.
            return xNameParts.Count - yNameParts.Count;
        }
    }
 
    private static IList<SimpleNameSyntax> DecomposeNameParts(NameSyntax name)
    {
        var result = new List<SimpleNameSyntax>();
        DecomposeNameParts(name, result);
        return result;
    }
 
    private static void DecomposeNameParts(NameSyntax name, List<SimpleNameSyntax> result)
    {
        switch (name.Kind())
        {
            case SyntaxKind.QualifiedName:
                var dottedName = (QualifiedNameSyntax)name;
                DecomposeNameParts(dottedName.Left, result);
                DecomposeNameParts(dottedName.Right, result);
                break;
            case SyntaxKind.AliasQualifiedName:
                var aliasedName = (AliasQualifiedNameSyntax)name;
                result.Add(aliasedName.Alias);
                DecomposeNameParts(aliasedName.Name, result);
                break;
            case SyntaxKind.IdentifierName:
                result.Add((IdentifierNameSyntax)name);
                break;
            case SyntaxKind.GenericName:
                result.Add((GenericNameSyntax)name);
                break;
        }
    }
 
    private int Compare(GenericNameSyntax x, GenericNameSyntax y)
    {
        var compare = _tokenComparer.Compare(x.Identifier, y.Identifier);
        if (compare != 0)
            return compare;
 
        // The one with less type params comes first.
        compare = x.Arity - y.Arity;
        if (compare != 0)
            return compare;
 
        // Same name, same parameter count.  Compare each parameter.
        for (var i = 0; i < x.Arity; i++)
        {
            var xArg = x.TypeArgumentList.Arguments[i];
            var yArg = y.TypeArgumentList.Arguments[i];
 
            compare = TypeComparer.Compare(xArg, yArg);
            if (compare != 0)
                return compare;
        }
 
        return 0;
    }
}