mirror of
https://github.com/Stone-Red-Code/YesNt-Interpreter.git
synced 2026-09-04 09:06:41 +02:00
Add property initializer default value support in generated classes
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -30,7 +31,7 @@ public sealed class StatementRegistryGenerator : IIncrementalGenerator
|
||||
INamedTypeSymbol? statementAttrSymbol = compilation.GetTypeByMetadataName(StatementAttributeName);
|
||||
INamedTypeSymbol? staticStatementAttrSymbol = compilation.GetTypeByMetadataName(StaticStatementAttributeName);
|
||||
|
||||
string infoSource = GenerateInformationClassesSource(statementAttrSymbol, staticStatementAttrSymbol);
|
||||
string infoSource = GenerateInformationClassesSource(statementAttrSymbol, staticStatementAttrSymbol, compilation);
|
||||
context.AddSource("GeneratedStatementInformation.g.cs", infoSource);
|
||||
|
||||
string registrySource = GenerateRegistrySource(statementMethods, staticStatementMethods);
|
||||
@@ -89,7 +90,8 @@ public sealed class StatementRegistryGenerator : IIncrementalGenerator
|
||||
|
||||
private static string GenerateInformationClassesSource(
|
||||
INamedTypeSymbol? statementAttrSymbol,
|
||||
INamedTypeSymbol? staticStatementAttrSymbol)
|
||||
INamedTypeSymbol? staticStatementAttrSymbol,
|
||||
Compilation compilation)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
@@ -101,12 +103,12 @@ public sealed class StatementRegistryGenerator : IIncrementalGenerator
|
||||
|
||||
if (statementAttrSymbol is not null)
|
||||
{
|
||||
EmitInformationClass(sb, statementAttrSymbol, "StatementInformation");
|
||||
EmitInformationClass(sb, statementAttrSymbol, "StatementInformation", compilation);
|
||||
}
|
||||
|
||||
if (staticStatementAttrSymbol is not null)
|
||||
{
|
||||
EmitInformationClass(sb, staticStatementAttrSymbol, "StaticStatementInformation");
|
||||
EmitInformationClass(sb, staticStatementAttrSymbol, "StaticStatementInformation", compilation);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
@@ -115,7 +117,8 @@ public sealed class StatementRegistryGenerator : IIncrementalGenerator
|
||||
private static void EmitInformationClass(
|
||||
StringBuilder sb,
|
||||
INamedTypeSymbol attributeSymbol,
|
||||
string className)
|
||||
string className,
|
||||
Compilation compilation)
|
||||
{
|
||||
List<IMethodSymbol> ctors = attributeSymbol.Constructors
|
||||
.Where(c => !c.IsImplicitlyDeclared)
|
||||
@@ -186,7 +189,7 @@ public sealed class StatementRegistryGenerator : IIncrementalGenerator
|
||||
// ---- settable properties (named-argument style) ----
|
||||
foreach (IPropertySymbol prop in settableProps)
|
||||
{
|
||||
string defaultClause = GetDefaultClause(prop);
|
||||
string defaultClause = GetDefaultClause(prop, compilation);
|
||||
_ = sb.AppendLine($" public {GlobalType(prop.Type)} {prop.Name} {{ get; init; }}{defaultClause}");
|
||||
}
|
||||
|
||||
@@ -244,11 +247,47 @@ public sealed class StatementRegistryGenerator : IIncrementalGenerator
|
||||
return string.IsNullOrEmpty(name) ? name : char.ToUpperInvariant(name[0]) + name.Substring(1);
|
||||
}
|
||||
|
||||
private static string GetDefaultClause(IPropertySymbol prop)
|
||||
private static string GetDefaultClause(
|
||||
IPropertySymbol prop,
|
||||
Compilation compilation)
|
||||
{
|
||||
// Only emit a default for a handful of well-known "safe" defaults so the
|
||||
// generated code compiles even when the caller omits the named argument.
|
||||
return prop.Type.IsReferenceType || prop.Type.NullableAnnotation == NullableAnnotation.Annotated
|
||||
foreach (SyntaxReference syntaxRef in prop.DeclaringSyntaxReferences)
|
||||
{
|
||||
if (syntaxRef.GetSyntax() is not Microsoft.CodeAnalysis.CSharp.Syntax.PropertyDeclarationSyntax syntax)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (syntax.Initializer?.Value is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
SemanticModel model = compilation.GetSemanticModel(syntax.SyntaxTree);
|
||||
ExpressionSyntax expr = syntax.Initializer.Value;
|
||||
|
||||
// 1. Try constant evaluation first (SAFE)
|
||||
Optional<object?> constant = model.GetConstantValue(expr);
|
||||
if (constant.HasValue)
|
||||
{
|
||||
return $" = {ToLiteral(constant.Value!, prop.Type)};";
|
||||
}
|
||||
|
||||
// 2. Try symbol resolution (enum fields etc.)
|
||||
ISymbol? symbolInfo = model.GetSymbolInfo(expr).Symbol;
|
||||
|
||||
if (symbolInfo is IFieldSymbol field)
|
||||
{
|
||||
string typeName = GlobalType(field.ContainingType);
|
||||
return $" = {typeName}.{field.Name};";
|
||||
}
|
||||
|
||||
// 3. fallback: raw expression (last resort)
|
||||
return $" = {expr};";
|
||||
}
|
||||
|
||||
// fallback defaults
|
||||
return prop.Type.IsReferenceType || prop.NullableAnnotation == NullableAnnotation.Annotated
|
||||
? " = null!;"
|
||||
: prop.Type.SpecialType switch
|
||||
{
|
||||
@@ -391,6 +430,38 @@ public sealed class StatementRegistryGenerator : IIncrementalGenerator
|
||||
};
|
||||
}
|
||||
|
||||
private static string ToLiteral(object value, ITypeSymbol type)
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
return "null!";
|
||||
}
|
||||
|
||||
if (type.TypeKind == TypeKind.Enum)
|
||||
{
|
||||
string enumType = GlobalType(type);
|
||||
|
||||
// value is already boxed enum OR underlying integral type
|
||||
long underlying = Convert.ToInt64(value);
|
||||
|
||||
// fallback: cast
|
||||
return $"({enumType}){underlying}";
|
||||
}
|
||||
|
||||
return type.SpecialType switch
|
||||
{
|
||||
SpecialType.System_String => $"\"{EscapeString((string)value)}\"",
|
||||
SpecialType.System_Char => $"'{EscapeChar((char)value)}'",
|
||||
SpecialType.System_Boolean => (bool)value ? "true" : "false",
|
||||
SpecialType.System_Int32 => ((int)value).ToString(System.Globalization.CultureInfo.InvariantCulture),
|
||||
SpecialType.System_Int64 => ((long)value).ToString(System.Globalization.CultureInfo.InvariantCulture) + "L",
|
||||
SpecialType.System_Single => ((float)value).ToString(System.Globalization.CultureInfo.InvariantCulture) + "f",
|
||||
SpecialType.System_Double => ((double)value).ToString(System.Globalization.CultureInfo.InvariantCulture),
|
||||
SpecialType.System_Decimal => ((decimal)value).ToString(System.Globalization.CultureInfo.InvariantCulture) + "m",
|
||||
_ => value.ToString() ?? "null!"
|
||||
};
|
||||
}
|
||||
|
||||
private static string EscapeString(string value)
|
||||
{
|
||||
return value
|
||||
|
||||
Reference in New Issue
Block a user