mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-14 19:29:36 +01:00
332 lines
9.5 KiB
C#
332 lines
9.5 KiB
C#
using System.Collections.Generic;
|
|
using Microsoft.CodeAnalysis;
|
|
using Microsoft.CodeAnalysis.CSharp;
|
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
|
|
|
namespace Robust.Serialization.Generator;
|
|
|
|
internal static class Types
|
|
{
|
|
private const string DataDefinitionNamespace = "Robust.Shared.Serialization.Manager.Attributes.DataDefinitionAttribute";
|
|
private const string ImplicitDataDefinitionNamespace = "Robust.Shared.Serialization.Manager.Attributes.ImplicitDataDefinitionForInheritorsAttribute";
|
|
private const string DataFieldBaseNamespace = "Robust.Shared.Serialization.Manager.Attributes.DataFieldBaseAttribute";
|
|
private const string CopyByRefNamespace = "Robust.Shared.Serialization.Manager.Attributes.CopyByRefAttribute";
|
|
|
|
internal static bool IsPartial(TypeDeclarationSyntax type)
|
|
{
|
|
return type.Modifiers.IndexOf(SyntaxKind.PartialKeyword) != -1;
|
|
}
|
|
|
|
internal static bool IsDataDefinition(ITypeSymbol? type)
|
|
{
|
|
if (type == null)
|
|
return false;
|
|
|
|
return HasAttribute(type, DataDefinitionNamespace) ||
|
|
IsImplicitDataDefinition(type);
|
|
}
|
|
|
|
internal static bool IsDataField(ISymbol member, out ITypeSymbol type, out AttributeData attribute)
|
|
{
|
|
// TODO data records and other attributes
|
|
if (member is IFieldSymbol field)
|
|
{
|
|
foreach (var attr in field.GetAttributes())
|
|
{
|
|
if (attr.AttributeClass != null && Inherits(attr.AttributeClass, DataFieldBaseNamespace))
|
|
{
|
|
type = field.Type;
|
|
attribute = attr;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
else if (member is IPropertySymbol property)
|
|
{
|
|
foreach (var attr in property.GetAttributes())
|
|
{
|
|
if (attr.AttributeClass != null && Inherits(attr.AttributeClass, DataFieldBaseNamespace))
|
|
{
|
|
type = property.Type;
|
|
attribute = attr;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
type = null!;
|
|
attribute = null!;
|
|
return false;
|
|
}
|
|
|
|
internal static bool IsImplicitDataDefinition(ITypeSymbol type)
|
|
{
|
|
if (HasAttribute(type, ImplicitDataDefinitionNamespace))
|
|
return true;
|
|
|
|
foreach (var baseType in GetBaseTypes(type))
|
|
{
|
|
if (HasAttribute(baseType, ImplicitDataDefinitionNamespace))
|
|
return true;
|
|
}
|
|
|
|
foreach (var @interface in type.AllInterfaces)
|
|
{
|
|
if (IsImplicitDataDefinitionInterface(@interface))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
internal static bool IsImplicitDataDefinitionInterface(ITypeSymbol @interface)
|
|
{
|
|
if (HasAttribute(@interface, ImplicitDataDefinitionNamespace))
|
|
return true;
|
|
|
|
foreach (var subInterface in @interface.AllInterfaces)
|
|
{
|
|
if (HasAttribute(subInterface, ImplicitDataDefinitionNamespace))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
internal static IEnumerable<ITypeSymbol> GetImplicitDataDefinitionInterfaces(ITypeSymbol type, bool all)
|
|
{
|
|
var interfaces = all ? type.AllInterfaces : type.Interfaces;
|
|
foreach (var @interface in interfaces)
|
|
{
|
|
if (IsImplicitDataDefinitionInterface(@interface))
|
|
yield return @interface;
|
|
}
|
|
}
|
|
|
|
internal static bool IsNullableType(ITypeSymbol type)
|
|
{
|
|
if (type.NullableAnnotation == NullableAnnotation.Annotated)
|
|
return true;
|
|
|
|
if (type.OriginalDefinition.ToDisplayString() == "System.Nullable<T>")
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
internal static bool IsNullableValueType(ITypeSymbol type)
|
|
{
|
|
return type.IsValueType && IsNullableType(type);
|
|
}
|
|
|
|
internal static bool IsMultidimensionalArray(ITypeSymbol type)
|
|
{
|
|
return type is IArrayTypeSymbol { Rank: > 1 };
|
|
}
|
|
|
|
internal static bool CanBeCopiedByValue(ISymbol member, ITypeSymbol type)
|
|
{
|
|
if (type.OriginalDefinition.ToDisplayString() == "System.Nullable<T>")
|
|
return CanBeCopiedByValue(member, ((INamedTypeSymbol) type).TypeArguments[0]);
|
|
|
|
if (type.TypeKind == TypeKind.Enum)
|
|
return true;
|
|
|
|
switch (type.SpecialType)
|
|
{
|
|
case SpecialType.System_Enum:
|
|
case SpecialType.System_Boolean:
|
|
case SpecialType.System_Char:
|
|
case SpecialType.System_SByte:
|
|
case SpecialType.System_Byte:
|
|
case SpecialType.System_Int16:
|
|
case SpecialType.System_UInt16:
|
|
case SpecialType.System_Int32:
|
|
case SpecialType.System_UInt32:
|
|
case SpecialType.System_Int64:
|
|
case SpecialType.System_UInt64:
|
|
case SpecialType.System_Decimal:
|
|
case SpecialType.System_Single:
|
|
case SpecialType.System_Double:
|
|
case SpecialType.System_String:
|
|
case SpecialType.System_DateTime:
|
|
return true;
|
|
}
|
|
|
|
if (HasAttribute(member, CopyByRefNamespace))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
internal static string GetGenericTypeName(ITypeSymbol symbol)
|
|
{
|
|
var name = symbol.Name;
|
|
|
|
if (symbol is INamedTypeSymbol { TypeParameters: { Length: > 0 } parameters })
|
|
{
|
|
name += "<";
|
|
|
|
for (var i = 0; i < parameters.Length; i++)
|
|
{
|
|
var parameter = parameters[i];
|
|
name += parameter.Name;
|
|
|
|
if (i < parameters.Length - 1)
|
|
{
|
|
name += ", ";
|
|
}
|
|
}
|
|
|
|
name += ">";
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
internal static string GetPartialTypeDefinitionLine(ITypeSymbol symbol)
|
|
{
|
|
var access = symbol.DeclaredAccessibility switch
|
|
{
|
|
Accessibility.Private => "private",
|
|
Accessibility.ProtectedAndInternal => "protected internal",
|
|
Accessibility.Protected => "protected",
|
|
Accessibility.Internal => "internal",
|
|
Accessibility.Public => "public",
|
|
_ => "public"
|
|
};
|
|
|
|
var typeKeyword = "partial ";
|
|
if (symbol.TypeKind == TypeKind.Interface)
|
|
{
|
|
typeKeyword += "interface";
|
|
}
|
|
else
|
|
{
|
|
if (symbol.IsRecord)
|
|
{
|
|
typeKeyword += symbol.IsValueType ? "record struct" : "record";
|
|
}
|
|
else
|
|
{
|
|
typeKeyword += symbol.IsValueType ? "struct" : "class";
|
|
}
|
|
|
|
if (symbol.IsAbstract)
|
|
{
|
|
typeKeyword = $"abstract {typeKeyword}";
|
|
}
|
|
}
|
|
|
|
var typeName = GetGenericTypeName(symbol);
|
|
return $"{access} {typeKeyword} {typeName}";
|
|
}
|
|
|
|
internal static bool Inherits(ITypeSymbol type, string parent)
|
|
{
|
|
foreach (var baseType in GetBaseTypes(type))
|
|
{
|
|
if (baseType.ToDisplayString() == parent)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
internal static bool ImplementsInterface(ITypeSymbol type, string interfaceName)
|
|
{
|
|
foreach (var interfaceType in type.AllInterfaces)
|
|
{
|
|
if (interfaceType.ToDisplayString().Contains(interfaceName))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
internal static bool IsReadOnlyMember(ITypeSymbol type, ISymbol member)
|
|
{
|
|
if (member is IFieldSymbol field)
|
|
{
|
|
return field.IsReadOnly;
|
|
}
|
|
else if (member is IPropertySymbol property)
|
|
{
|
|
if (property.SetMethod == null)
|
|
return true;
|
|
|
|
if (property.SetMethod.IsInitOnly)
|
|
return type.IsReferenceType;
|
|
|
|
return false;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
internal static bool NeedsEmptyConstructor(ITypeSymbol type)
|
|
{
|
|
if (type is not INamedTypeSymbol named)
|
|
return false;
|
|
|
|
if (named.InstanceConstructors.Length == 0)
|
|
return true;
|
|
|
|
foreach (var constructor in named.InstanceConstructors)
|
|
{
|
|
if (constructor.Parameters.Length == 0 &&
|
|
!constructor.IsImplicitlyDeclared)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
internal static bool HasEmptyPublicConstructor(ITypeSymbol type)
|
|
{
|
|
if (type is not INamedTypeSymbol named)
|
|
return false;
|
|
|
|
foreach (var constructor in named.InstanceConstructors)
|
|
{
|
|
if (constructor.DeclaredAccessibility == Accessibility.Public &&
|
|
constructor.Parameters.Length == 0)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
internal static bool IsVirtualClass(ITypeSymbol type)
|
|
{
|
|
return type.IsReferenceType && !type.IsSealed && type.TypeKind != TypeKind.Interface;
|
|
}
|
|
|
|
internal static bool HasAttribute(ISymbol symbol, string attributeName)
|
|
{
|
|
foreach (var attribute in symbol.GetAttributes())
|
|
{
|
|
if (attribute.AttributeClass?.ToDisplayString() == attributeName)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
internal static IEnumerable<ITypeSymbol> GetBaseTypes(ITypeSymbol type)
|
|
{
|
|
var baseType = type.BaseType;
|
|
while (baseType != null)
|
|
{
|
|
yield return baseType;
|
|
baseType = baseType.BaseType;
|
|
}
|
|
}
|
|
}
|