Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions generator.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@
"SDL_bool": null
}
},
"TransformProperties": {
"BoolTypes": {
"SDL_bool": null
}
},
"IdentifySharedPrefixes": {
"GlobalPrefixHints": ["SDL"]
},
Expand Down Expand Up @@ -224,6 +229,11 @@
"GLboolean": null
}
},
"TransformProperties": {
"BoolTypes": {
"GLboolean": null
}
},
"StripAttributes": {
"Remove": [
"NativeTypeName",
Expand Down Expand Up @@ -361,6 +371,12 @@
},
"BenefitOfTheDoubtArrayTransformation": true
},
"TransformProperties": {
"BoolTypes": {
"ALboolean": null,
"ALCboolean": null
}
},
"TransformHandles": {
"UseDsl": true
},
Expand Down Expand Up @@ -434,6 +450,11 @@
"VkBool32": null
}
},
"TransformProperties": {
"BoolTypes": {
"VkBool32": null
}
},
"TransformHandles": {
"UseDsl": true
},
Expand Down
13 changes: 5 additions & 8 deletions sources/SilkTouch/SilkTouch/Mods/TransformFunctions.cs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -30,9 +27,9 @@ public class Configuration
public required bool IntReturnsMaybeBool { get; init; }

/// <summary>
/// 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.
/// </summary>
public Dictionary<string, string?>? BoolTypes { get; init; }
public Dictionary<string, string?> BoolTypes { get; init; } = [];
}

/// <inheritdoc />
Expand Down
99 changes: 89 additions & 10 deletions sources/SilkTouch/SilkTouch/Mods/TransformProperties.cs
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// Applies transformations to property signatures.
/// Applies transformations to fields and properties.
/// </summary>
/// <remarks>
/// Today, this only includes transforming properties like <c>static ReadOnlySpan&lt;byte&gt; Thing => "thing"u8;</c>
/// to be <c>static Utf8String Thing => "thing"u8;</c>.
/// Despite the name of the name, fields are also handled here because
/// they often need to be transformed alongside properties.
/// <para/>
/// This currently does the following transformations:
/// 1. Transform string constant properties like
/// <c>static ReadOnlySpan&lt;byte&gt; Thing => "thing"u8;</c> to be
/// <c>static Utf8String Thing => "thing"u8;</c>.
/// 2. Transform fields and properties that are recognised
/// to be akin to booleans to use the <c>MaybeBool</c> type.
/// This functionality is based on <see cref="BoolTransformer"/>.
/// </remarks>
public class TransformProperties : IMod
[ModConfiguration<Configuration>]
public class TransformProperties(IOptionsSnapshot<TransformProperties.Configuration> cfg) : IMod
{
/// <summary>
/// Configuration for the <see cref="TransformProperties"/>.
/// </summary>
public class Configuration
{
/// <summary>
/// Types to treat as boolean and their boolean schemes if different from the default.
/// </summary>
public Dictionary<string, string?> BoolTypes { get; init; } = [];
}

/// <inheritdoc />
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 ?? [])
{
Expand All @@ -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<TypeSyntax>)
[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<TypeSyntax>)[node.Type, IdentifierName(scheme)]
)
)
);

node = node.WithType(newType);
}

// Transform ReadOnlySpan<byte> 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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -30,7 +27,7 @@ Action<MethodDeclarationSyntax> 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...
)
{
Expand Down Expand Up @@ -63,7 +60,7 @@ Action<MethodDeclarationSyntax> 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...
)
)
Expand Down
Loading