diff --git a/generator.json b/generator.json index 1d08ab7d61..6c68161c18 100644 --- a/generator.json +++ b/generator.json @@ -68,6 +68,11 @@ "SDL_bool": null } }, + "TransformProperties": { + "BoolTypes": { + "SDL_bool": null + } + }, "IdentifySharedPrefixes": { "GlobalPrefixHints": ["SDL"] }, @@ -224,6 +229,11 @@ "GLboolean": null } }, + "TransformProperties": { + "BoolTypes": { + "GLboolean": null + } + }, "StripAttributes": { "Remove": [ "NativeTypeName", @@ -361,6 +371,12 @@ }, "BenefitOfTheDoubtArrayTransformation": true }, + "TransformProperties": { + "BoolTypes": { + "ALboolean": null, + "ALCboolean": null + } + }, "TransformHandles": { "UseDsl": true }, @@ -434,6 +450,11 @@ "VkBool32": null } }, + "TransformProperties": { + "BoolTypes": { + "VkBool32": null + } + }, "TransformHandles": { "UseDsl": true }, diff --git a/sources/SilkTouch/SilkTouch/Mods/TransformFunctions.cs b/sources/SilkTouch/SilkTouch/Mods/TransformFunctions.cs index 1d97c3a230..e106d5ad98 100644 --- a/sources/SilkTouch/SilkTouch/Mods/TransformFunctions.cs +++ b/sources/SilkTouch/SilkTouch/Mods/TransformFunctions.cs @@ -1,11 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Silk.NET.SilkTouch.Clang; using Silk.NET.SilkTouch.Mods.Transformation; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; @@ -30,9 +27,9 @@ public class Configuration public required bool IntReturnsMaybeBool { get; init; } /// - /// Types to treat as boolean and their boolean schemes if different to default. + /// Types to treat as boolean and their boolean schemes if different from the default. /// - public Dictionary? BoolTypes { get; init; } + public Dictionary BoolTypes { get; init; } = []; } /// diff --git a/sources/SilkTouch/SilkTouch/Mods/TransformProperties.cs b/sources/SilkTouch/SilkTouch/Mods/TransformProperties.cs index 4ce2a40291..ac49ebb763 100644 --- a/sources/SilkTouch/SilkTouch/Mods/TransformProperties.cs +++ b/sources/SilkTouch/SilkTouch/Mods/TransformProperties.cs @@ -1,29 +1,50 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.Extensions.Options; +using Silk.NET.SilkTouch.Mods.Transformation; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace Silk.NET.SilkTouch.Mods; /// -/// Applies transformations to property signatures. +/// Applies transformations to fields and properties. /// /// -/// Today, this only includes transforming properties like static ReadOnlySpan<byte> Thing => "thing"u8; -/// to be static Utf8String Thing => "thing"u8;. +/// Despite the name of the name, fields are also handled here because +/// they often need to be transformed alongside properties. +/// +/// This currently does the following transformations: +/// 1. Transform string constant properties like +/// static ReadOnlySpan<byte> Thing => "thing"u8; to be +/// static Utf8String Thing => "thing"u8;. +/// 2. Transform fields and properties that are recognised +/// to be akin to booleans to use the MaybeBool type. +/// This functionality is based on . /// -public class TransformProperties : IMod +[ModConfiguration] +public class TransformProperties(IOptionsSnapshot cfg) : IMod { + /// + /// Configuration for the . + /// + public class Configuration + { + /// + /// Types to treat as boolean and their boolean schemes if different from the default. + /// + public Dictionary BoolTypes { get; init; } = []; + } + /// public async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default) { - var rw = new Rewriter(); + var config = cfg.Get(ctx.JobKey); + + var rw = new Rewriter(config); var proj = ctx.SourceProject; foreach (var docId in ctx.SourceProject?.DocumentIds ?? []) { @@ -38,17 +59,75 @@ public async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default) ctx.SourceProject = proj; } - private class Rewriter : CSharpSyntaxRewriter + private class Rewriter(Configuration config) : CSharpSyntaxRewriter { + public override SyntaxNode? VisitFieldDeclaration(FieldDeclarationSyntax node) + { + // Transform bool-like fields to use MaybeBool + var nativeType = + node.AttributeLists.GetNativeTypeName() ?? node.Declaration.Type.ToString(); + if ( + config.BoolTypes.TryGetValue(nativeType, out var scheme) + || (nativeType == "bool" && node.Declaration.Type.ToString().Trim() != "bool") // stdbool.h, hopefully... + ) + { + var newType = string.IsNullOrWhiteSpace(scheme) + ? GenericName( + Identifier("MaybeBool"), + TypeArgumentList(SingletonSeparatedList(node.Declaration.Type)) + ) + : GenericName( + Identifier("MaybeBool"), + TypeArgumentList( + SeparatedList( + // ReSharper disable once RedundantCast <-- false positive + (IEnumerable) + [node.Declaration.Type, IdentifierName(scheme)] + ) + ) + ); + + node = node.WithDeclaration(node.Declaration.WithType(newType)); + } + + return base.VisitFieldDeclaration(node); + } + public override SyntaxNode? VisitPropertyDeclaration(PropertyDeclarationSyntax node) { + // Transform bool-like properties to use MaybeBool + var nativeType = node.AttributeLists.GetNativeTypeName() ?? node.Type.ToString(); + if ( + config.BoolTypes.TryGetValue(nativeType, out var scheme) + || (nativeType == "bool" && node.Type.ToString().Trim() != "bool") // stdbool.h, hopefully... + ) + { + var newType = string.IsNullOrWhiteSpace(scheme) + ? GenericName( + Identifier("MaybeBool"), + TypeArgumentList(SingletonSeparatedList(node.Type)) + ) + : GenericName( + Identifier("MaybeBool"), + TypeArgumentList( + SeparatedList( + // ReSharper disable once RedundantCast <-- false positive + (IEnumerable)[node.Type, IdentifierName(scheme)] + ) + ) + ); + + node = node.WithType(newType); + } + + // Transform ReadOnlySpan string constants to use Utf8String if ( node.Modifiers.Any(SyntaxKind.StaticKeyword) && node.Type is GenericNameSyntax { TypeArgumentList.Arguments: [PredefinedTypeSyntax pt], - Identifier.Text: "ReadOnlySpan" + Identifier.Text: "ReadOnlySpan", } && ( pt.Keyword.IsKind(SyntaxKind.ByteKeyword) diff --git a/sources/SilkTouch/SilkTouch/Mods/Transformation/BoolTransformer.cs b/sources/SilkTouch/SilkTouch/Mods/Transformation/BoolTransformer.cs index 8519678e15..f80eabf678 100644 --- a/sources/SilkTouch/SilkTouch/Mods/Transformation/BoolTransformer.cs +++ b/sources/SilkTouch/SilkTouch/Mods/Transformation/BoolTransformer.cs @@ -1,9 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; -using System.Diagnostics; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.Extensions.Options; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; @@ -30,7 +27,7 @@ Action next var retNative = current.GetNativeReturnTypeName() ?? current.ReturnType.ToString(); if ( (current.ReturnType.IsInteger() && cfg.IntReturnsMaybeBool) - || (cfg.BoolTypes?.TryGetValue(retNative, out retBoolScheme) ?? false) + || cfg.BoolTypes.TryGetValue(retNative, out retBoolScheme) || (retNative == "bool" && current.ReturnType.ToString().Trim() != "bool") // stdbool.h, hopefully... ) { @@ -63,7 +60,7 @@ Action next paramNative is not null && param.Type is not null && ( - (cfg.BoolTypes?.TryGetValue(paramNative, out paramBoolScheme) ?? false) + cfg.BoolTypes.TryGetValue(paramNative, out paramBoolScheme) || (paramNative == "bool" && param.Type.ToString().Trim() != "bool") // stdbool.h, hopefully... ) )