using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using XamlX.TypeSystem; namespace Robust.Client.NameGenerator { /// /// Taken from https://github.com/AvaloniaUI/Avalonia/blob/9d28762f1439e58e9764d104223b73eef93b26b5/src/tools/Avalonia.Generators/Compiler/RoslynTypeSystem.cs /// public class RoslynTypeSystem : IXamlTypeSystem { private readonly List _assemblies = new List(); public RoslynTypeSystem(CSharpCompilation compilation) { _assemblies.Add(new RoslynAssembly(compilation.Assembly)); var assemblySymbols = compilation .References .Select(compilation.GetAssemblyOrModuleSymbol) .OfType() .Select(assembly => new RoslynAssembly(assembly)) .ToList(); _assemblies.AddRange(assemblySymbols); } public IEnumerable Assemblies => _assemblies; public IXamlAssembly FindAssembly(string substring) => _assemblies[0]; public IXamlType? FindType(string name) { foreach (var assembly in _assemblies) { var type = assembly.FindType(name); if (type != null) return type; } return null; } public IXamlType? FindType(string name, string assembly) { foreach (var assemblyInstance in _assemblies) { var type = assemblyInstance.FindType(name); if (type != null) return type; } return null; } } public class RoslynAssembly : IXamlAssembly { private readonly IAssemblySymbol _symbol; public RoslynAssembly(IAssemblySymbol symbol) => _symbol = symbol; public bool Equals(IXamlAssembly other) => other is RoslynAssembly roslynAssembly && SymbolEqualityComparer.Default.Equals(_symbol, roslynAssembly._symbol); public string Name => _symbol.Name; public IReadOnlyList CustomAttributes => _symbol.GetAttributes() .Select(data => new RoslynAttribute(data, this)) .ToList(); public IXamlType? FindType(string fullName) { var type = _symbol.GetTypeByMetadataName(fullName); return type is null ? null : new RoslynType(type, this); } } public class RoslynAttribute : IXamlCustomAttribute { private readonly AttributeData _data; private readonly RoslynAssembly _assembly; public RoslynAttribute(AttributeData data, RoslynAssembly assembly) { _data = data; _assembly = assembly; } public bool Equals(IXamlCustomAttribute other) => other is RoslynAttribute attribute && _data == attribute._data; public IXamlType Type => new RoslynType(_data.AttributeClass!, _assembly); public List Parameters => _data.ConstructorArguments .Select(argument => argument.Value) .ToList(); public Dictionary Properties => _data.NamedArguments.ToDictionary( pair => pair.Key, pair => pair.Value.Value); } public class RoslynType : IXamlType { private static readonly SymbolDisplayFormat SymbolDisplayFormat = new SymbolDisplayFormat( typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters | SymbolDisplayGenericsOptions.IncludeTypeConstraints | SymbolDisplayGenericsOptions.IncludeVariance); private readonly RoslynAssembly _assembly; private readonly INamedTypeSymbol _symbol; public RoslynType(INamedTypeSymbol symbol, RoslynAssembly assembly) { _symbol = symbol; _assembly = assembly; } public bool Equals(IXamlType other) => other is RoslynType roslynType && SymbolEqualityComparer.Default.Equals(_symbol, roslynType._symbol); public object Id => _symbol; public string Name => _symbol.Name; public string Namespace => _symbol.ContainingNamespace.ToDisplayString(SymbolDisplayFormat); public string FullName => $"{Namespace}.{Name}"; public IXamlAssembly Assembly => _assembly; public IReadOnlyList Properties => _symbol.GetMembers() .Where(member => member.Kind == SymbolKind.Property) .OfType() .Select(property => new RoslynProperty(property, _assembly)) .ToList(); public IReadOnlyList Events { get; } = new List(); public IReadOnlyList Fields { get; } = new List(); public IReadOnlyList Methods { get; } = new List(); public IReadOnlyList Constructors => _symbol.Constructors .Select(method => new RoslynConstructor(method, _assembly)) .ToList(); public IReadOnlyList CustomAttributes { get; } = new List(); public IReadOnlyList GenericArguments { get; } = new List(); public bool IsAssignableFrom(IXamlType type) => type == this; public IXamlType MakeGenericType(IReadOnlyList typeArguments) => this; public IXamlType GenericTypeDefinition => this; public bool IsArray => false; public IXamlType? ArrayElementType { get; } = null; public IXamlType MakeArrayType(int dimensions) => throw new NotImplementedException(); public IXamlType? BaseType => _symbol.BaseType == null ? null : new RoslynType(_symbol.BaseType, _assembly); public bool IsValueType { get; } = false; public bool IsEnum { get; } = false; public bool IsPublic => _symbol.DeclaredAccessibility == Accessibility.Public; public bool IsNestedPrivate => _symbol.DeclaredAccessibility == Accessibility.Private; public bool IsFunctionPointer => false; public IXamlType? DeclaringType => _symbol.ContainingType is { } containingType ? new RoslynType(containingType, _assembly) : null; public IReadOnlyList Interfaces => _symbol.AllInterfaces .Select(abstraction => new RoslynType(abstraction, _assembly)) .ToList(); public bool IsInterface => _symbol.IsAbstract; public IXamlType GetEnumUnderlyingType() => throw new NotImplementedException(); public IReadOnlyList GenericParameters { get; } = new List(); } public class RoslynConstructor : IXamlConstructor { private readonly IMethodSymbol _symbol; private readonly RoslynAssembly _assembly; public RoslynConstructor(IMethodSymbol symbol, RoslynAssembly assembly) { _symbol = symbol; _assembly = assembly; } public bool Equals(IXamlConstructor other) => other is RoslynConstructor roslynConstructor && SymbolEqualityComparer.Default.Equals(_symbol, roslynConstructor._symbol); public bool IsPublic => true; public bool IsStatic => false; public IReadOnlyList Parameters => _symbol.Parameters .Select(parameter => parameter.Type) .OfType() .Select(type => new RoslynType(type, _assembly)) .ToList(); public string Name => _symbol.Name; public IXamlType DeclaringType => new RoslynType(_symbol.ContainingType, _assembly); public IXamlParameterInfo GetParameterInfo(int index) => new RoslynParameter(_assembly, _symbol.Parameters[index]); } public class RoslynProperty : IXamlProperty { private readonly IPropertySymbol _symbol; private readonly RoslynAssembly _assembly; public RoslynProperty(IPropertySymbol symbol, RoslynAssembly assembly) { _symbol = symbol; _assembly = assembly; } public bool Equals(IXamlProperty other) => other is RoslynProperty roslynProperty && SymbolEqualityComparer.Default.Equals(_symbol, roslynProperty._symbol); public string Name => _symbol.Name; public IXamlType PropertyType => _symbol.Type is INamedTypeSymbol namedTypeSymbol ? new RoslynType(namedTypeSymbol, _assembly) : throw new InvalidOperationException($"Roslyn property type {_symbol.Type} is not supported."); public IXamlType DeclaringType => new RoslynType(_symbol.ContainingType, _assembly); public IXamlMethod? Getter => _symbol.GetMethod == null ? null : new RoslynMethod(_symbol.GetMethod, _assembly); public IXamlMethod? Setter => _symbol.SetMethod == null ? null : new RoslynMethod(_symbol.SetMethod, _assembly); public IReadOnlyList CustomAttributes { get; } = new List(); public IReadOnlyList IndexerParameters { get; } = new List(); } public class RoslynParameter : IXamlParameterInfo { private readonly RoslynAssembly _assembly; private readonly IParameterSymbol _symbol; public RoslynParameter(RoslynAssembly assembly, IParameterSymbol symbol) { _assembly = assembly; _symbol = symbol; } public string Name => _symbol.Name; public IXamlType ParameterType => new RoslynType((INamedTypeSymbol)_symbol.Type, _assembly); public IReadOnlyList CustomAttributes => Array.Empty(); } public class RoslynMethod : IXamlMethod { private readonly IMethodSymbol _symbol; private readonly RoslynAssembly _assembly; public RoslynMethod(IMethodSymbol symbol, RoslynAssembly assembly) { _symbol = symbol; _assembly = assembly; } public bool Equals(IXamlMethod other) => other is RoslynMethod roslynMethod && SymbolEqualityComparer.Default.Equals(roslynMethod._symbol, _symbol); public string Name => _symbol.Name; public bool IsPublic => true; public bool IsPrivate => _symbol.DeclaredAccessibility == Accessibility.Private; public bool IsFamily => _symbol.DeclaredAccessibility == Accessibility.Protected; public bool ContainsGenericParameters => _symbol.TypeParameters.Any(); public bool IsGenericMethod => _symbol.IsGenericMethod; public bool IsGenericMethodDefinition => _symbol.IsDefinition && _symbol.IsGenericMethod; public bool IsStatic => false; public IXamlType ReturnType => new RoslynType((INamedTypeSymbol) _symbol.ReturnType, _assembly); public IReadOnlyList Parameters => _symbol.Parameters.Select(parameter => parameter.Type) .OfType() .Select(type => new RoslynType(type, _assembly)) .ToList(); public IXamlType DeclaringType => new RoslynType((INamedTypeSymbol)_symbol.ReceiverType! as INamedTypeSymbol, _assembly); public IReadOnlyList GenericParameters => throw new NotImplementedException(); public IReadOnlyList GenericArguments => _symbol.TypeArguments .Select(ga => new RoslynType((INamedTypeSymbol)ga, _assembly)) .ToArray(); public IXamlMethod MakeGenericMethod(IReadOnlyList typeArguments) => throw new NotImplementedException(); public IReadOnlyList CustomAttributes { get; } = new List(); public IXamlParameterInfo GetParameterInfo(int index) => new RoslynParameter(_assembly, _symbol.Parameters[index]); } }