mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-14 19:29:36 +01:00
Slightly less bad use of incremental generators. Remove format of generated code, it was taking most of the CPU time. This seems to significantly cut compile time cost of this generator. Nice. I'm seeing 20 -> 5 seconds on my system.
331 lines
9.5 KiB
C#
331 lines
9.5 KiB
C#
using System.Collections.Generic;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
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<string> GetImplicitDataDefinitionInterfaces(ITypeSymbol type, bool all)
|
|
{
|
|
var interfaces = all ? type.AllInterfaces : type.Interfaces;
|
|
foreach (var @interface in interfaces)
|
|
{
|
|
if (IsImplicitDataDefinitionInterface(@interface))
|
|
yield return @interface.ToDisplayString();
|
|
}
|
|
}
|
|
|
|
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 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 bool TryGetAttribute(ISymbol symbol, string attributeName, [NotNullWhen(true)] out AttributeData? data)
|
|
{
|
|
foreach (var attribute in symbol.GetAttributes())
|
|
{
|
|
if (attribute.AttributeClass?.ToDisplayString() == attributeName)
|
|
{
|
|
data = attribute;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
data = null;
|
|
return false;
|
|
}
|
|
|
|
internal static IEnumerable<ITypeSymbol> GetBaseTypes(ITypeSymbol type)
|
|
{
|
|
var baseType = type.BaseType;
|
|
while (baseType != null)
|
|
{
|
|
yield return baseType;
|
|
baseType = baseType.BaseType;
|
|
}
|
|
}
|
|
}
|