|
// 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 disable
using System;
using System.Collections.Concurrent;
using System.Collections.Immutable;
using System.Globalization;
using System.Linq;
using Microsoft.CodeAnalysis.LanguageService;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Shared.Extensions;
internal static partial class ISymbolExtensions2
{
[Obsolete("Use overload without ISymbolDisplayService")]
public static ImmutableArray<TSymbol> Sort<TSymbol>(
this ImmutableArray<TSymbol> symbols,
ISymbolDisplayService symbolDisplayService,
SemanticModel semanticModel,
int position)
where TSymbol : ISymbol
{
return Sort(symbols, semanticModel, position);
}
public static ImmutableArray<TSymbol> Sort<TSymbol>(
this ImmutableArray<TSymbol> symbols,
SemanticModel semanticModel,
int position)
where TSymbol : ISymbol
{
var symbolToParameterTypeNames = new ConcurrentDictionary<TSymbol, string[]>();
string[] getParameterTypeNames(TSymbol s) => GetParameterTypeNames(s, semanticModel, position);
return [.. symbols.OrderBy((s1, s2) => Compare(s1, s2, symbolToParameterTypeNames, getParameterTypeNames))];
}
private static INamedTypeSymbol GetNamedType(ITypeSymbol type)
=> type switch
{
INamedTypeSymbol namedType => namedType,
IArrayTypeSymbol arrayType => GetNamedType(arrayType.ElementType),
IPointerTypeSymbol pointerType => GetNamedType(pointerType.PointedAtType),
_ => null,
};
private static int CompareParameters(
ImmutableArray<IParameterSymbol> xParameters, string[] xTypeNames,
ImmutableArray<IParameterSymbol> yParameters, string[] yTypeNames)
{
// * Order by the number of parameters
// * If the same number of parameters...
// * Sort alphabetically by parameter type name
// * Params parameters are sorted at the end
// * Parameter types with type parameters are after those
if (xParameters.IsDefault || yParameters.IsDefault)
{
return xParameters.IsDefault && yParameters.IsDefault ? 0 :
xParameters.IsDefault ? -1 : 1;
}
var diff = xParameters.Length - yParameters.Length;
if (diff != 0)
{
return diff;
}
var paramCount = xParameters.Length;
for (var i = 0; i < paramCount; i++)
{
var xParam = xParameters[i];
var yParam = yParameters[i];
var xParamType = GetNamedType(xParam.Type);
var yParamType = GetNamedType(yParam.Type);
if (xParamType != null && yParamType != null)
{
diff = CompareNamedTypes(xParamType, yParamType);
if (diff != 0)
{
return diff;
}
}
if (xParam.IsParams != yParam.IsParams)
{
return xParam.IsParams ? 1 : -1;
}
diff = CultureInfo.CurrentUICulture.CompareInfo.Compare(xTypeNames[i], yTypeNames[i], CompareOptions.StringSort);
if (diff != 0)
{
return diff;
}
}
return 0;
}
private static int CompareProperties(IPropertySymbol xProperty, string[] xTypeNames, IPropertySymbol yProperty, string[] yTypeNames)
=> CompareParameters(xProperty.Parameters, xTypeNames, yProperty.Parameters, yTypeNames);
private static int CompareMethods(IMethodSymbol xMethod, string[] xTypeNames, IMethodSymbol yMethod, string[] yTypeNames)
{
// * Order by arity
// * Order by parameters
var diff = xMethod.Arity - yMethod.Arity;
if (diff != 0)
{
return diff;
}
return CompareParameters(xMethod.Parameters, xTypeNames, yMethod.Parameters, yTypeNames);
}
private static int CompareEvents(IEventSymbol xEvent, string[] xTypeNames, IEventSymbol yEvent, string[] yTypeNames)
=> CompareParameters(GetMethodOrIndexerOrEventParameters(xEvent), xTypeNames, GetMethodOrIndexerOrEventParameters(yEvent), yTypeNames);
private static int CompareNamedTypes(INamedTypeSymbol xNamedType, INamedTypeSymbol yNamedType)
{
// For named types, we sort on arity.
return xNamedType.Arity - yNamedType.Arity;
}
private static string[] GetParameterTypeNames(
ISymbol symbol,
SemanticModel semanticModel,
int position)
{
return GetMethodOrIndexerOrEventParameters(symbol)
.Select(p => p.Type.ToMinimalDisplayString(semanticModel, position))
.ToArray();
}
private static ImmutableArray<IParameterSymbol> GetMethodOrIndexerOrEventParameters(ISymbol symbol)
{
if (symbol is IEventSymbol ev)
{
var type = ev.Type as INamedTypeSymbol;
if (type.IsDelegateType())
{
return type.DelegateInvokeMethod.Parameters;
}
}
return symbol.GetParameters();
}
private static int Compare<TSymbol>(
TSymbol s1, TSymbol s2,
ConcurrentDictionary<TSymbol, string[]> symbolToParameterTypeNames,
Func<TSymbol, string[]> getParameterTypeNames)
where TSymbol : ISymbol
{
var symbol1ParameterTypeNames = symbolToParameterTypeNames.GetOrAdd(s1, getParameterTypeNames);
var symbol2ParameterTypeNames = symbolToParameterTypeNames.GetOrAdd(s2, getParameterTypeNames);
// Order named types before methods and properties, and methods before properties.
if (s1.Kind == SymbolKind.NamedType || s2.Kind == SymbolKind.NamedType)
{
return s1.Kind == s2.Kind
? CompareNamedTypes((INamedTypeSymbol)s1, (INamedTypeSymbol)s2)
: s1.Kind == SymbolKind.NamedType ? -1 : 1;
}
if (s1.Kind == SymbolKind.Method || s2.Kind == SymbolKind.Method)
{
return s1.Kind == s2.Kind
? CompareMethods((IMethodSymbol)s1, symbol1ParameterTypeNames, (IMethodSymbol)s2, symbol2ParameterTypeNames)
: s1.Kind == SymbolKind.Method ? -1 : 1;
}
if (s1.Kind == SymbolKind.Property || s2.Kind == SymbolKind.Property)
{
return s1.Kind == s2.Kind
? CompareProperties((IPropertySymbol)s1, symbol1ParameterTypeNames, (IPropertySymbol)s2, symbol2ParameterTypeNames)
: s1.Kind == SymbolKind.Property ? -1 : 1;
}
if (s1.Kind == SymbolKind.Event || s2.Kind == SymbolKind.Event)
{
return s1.Kind == s2.Kind
? CompareEvents((IEventSymbol)s1, symbol1ParameterTypeNames, (IEventSymbol)s2, symbol2ParameterTypeNames)
: s1.Kind == SymbolKind.Event ? -1 : 1;
}
throw ExceptionUtilities.UnexpectedValue((s1.Kind, s2.Kind));
}
}
|