From 634e7361d5b5c79e6af63bc97d7151bde076f8cf Mon Sep 17 00:00:00 2001 From: William Chen Date: Tue, 24 Mar 2026 06:36:01 -0400 Subject: [PATCH 1/9] Start of feature/nested-struct-name-affixes branch From 35f0f5961d45778951f85dbbfc326f5de6635beb Mon Sep 17 00:00:00 2001 From: William Chen Date: Wed, 25 Mar 2026 10:46:51 -0400 Subject: [PATCH 2/9] Cleanup leftover return statement --- sources/SilkTouch/SilkTouch/Mods/TransformHandles.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/sources/SilkTouch/SilkTouch/Mods/TransformHandles.cs b/sources/SilkTouch/SilkTouch/Mods/TransformHandles.cs index 5e958705c6..9f4e8782aa 100644 --- a/sources/SilkTouch/SilkTouch/Mods/TransformHandles.cs +++ b/sources/SilkTouch/SilkTouch/Mods/TransformHandles.cs @@ -165,8 +165,6 @@ [new PointerDimensionReductionTransformer()], } ctx.SourceProject = project; - - return; } private class MissingHandleTypeDiscoverer( From cd2ccfd4f7ac0ec5c36e69a207a3b694e2509660 Mon Sep 17 00:00:00 2001 From: William Chen Date: Wed, 25 Mar 2026 11:16:48 -0400 Subject: [PATCH 3/9] Split TransformHandles into two mods --- generator.json | 6 +- .../SilkTouch/Mods/Common/ModLoader.cs | 1 + .../SilkTouch/Mods/ExtractHandles.cs | 324 ++++++++++++++++++ .../SilkTouch/Mods/TransformHandles.cs | 306 +---------------- 4 files changed, 329 insertions(+), 308 deletions(-) create mode 100644 sources/SilkTouch/SilkTouch/Mods/ExtractHandles.cs diff --git a/generator.json b/generator.json index 8bbae05cda..2831592107 100644 --- a/generator.json +++ b/generator.json @@ -42,6 +42,7 @@ "AddIncludes", "ClangScraper", "MarkNativeNames", + "ExtractHandles", "ExtractNestedTyping", "TransformHandles", "TransformFunctions", @@ -59,7 +60,6 @@ "InputTestRoot": "tests/SDL" }, "TransformHandles": { - "AssumeMissingTypesOpaque": true, "UseDSL": true }, "TransformFunctions": { @@ -228,6 +228,7 @@ "ChangeNativeClass", "AddApiProfiles", "MixKhronosData", + "ExtractHandles", "ExtractNestedTyping", "TransformHandles", "InterceptNativeFunctions", @@ -351,7 +352,6 @@ "BenefitOfTheDoubtArrayTransformation": true }, "TransformHandles": { - "AssumeMissingTypesOpaque": true, "UseDSL": true }, "StripAttributes": { @@ -370,6 +370,7 @@ "AddIncludes", "ClangScraper", "MarkNativeNames", + "ExtractHandles", "ExtractNestedTyping", "TransformHandles", "MixKhronosData", @@ -423,7 +424,6 @@ } }, "TransformHandles": { - "AssumeMissingTypesOpaque": true, "UseDSL": true }, "PrettifyNames": { diff --git a/sources/SilkTouch/SilkTouch/Mods/Common/ModLoader.cs b/sources/SilkTouch/SilkTouch/Mods/Common/ModLoader.cs index 8556bd7539..1f454cb49b 100644 --- a/sources/SilkTouch/SilkTouch/Mods/Common/ModLoader.cs +++ b/sources/SilkTouch/SilkTouch/Mods/Common/ModLoader.cs @@ -25,6 +25,7 @@ public class ModLoader nameof(BakeSourceSets) => typeof(BakeSourceSets), nameof(AddApiProfiles) => typeof(AddApiProfiles), nameof(MixKhronosData) => typeof(MixKhronosData), + nameof(ExtractHandles) => typeof(ExtractHandles), nameof(TransformHandles) => typeof(TransformHandles), nameof(TransformEnums) => typeof(TransformEnums), nameof(ExtractNestedTyping) => typeof(ExtractNestedTyping), diff --git a/sources/SilkTouch/SilkTouch/Mods/ExtractHandles.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractHandles.cs new file mode 100644 index 0000000000..d1018b9caf --- /dev/null +++ b/sources/SilkTouch/SilkTouch/Mods/ExtractHandles.cs @@ -0,0 +1,324 @@ +// 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; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.Extensions.Logging; +using Silk.NET.SilkTouch.Naming; +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; + +namespace Silk.NET.SilkTouch.Mods; + +/// +/// Adds empty handle structs by searching for missing types referenced through pointers. +/// If all references to the missing type are through a pointer, +/// that missing type is then added as an empty struct. +/// +/// See for applying further transformations. +/// +public class ExtractHandles(ILogger logger) : Mod +{ + /// + public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default) + { + await base.ExecuteAsync(ctx, ct); + + var project = ctx.SourceProject; + if (project == null) + { + return; + } + + var compilation = await project.GetCompilationAsync(ct); + if (compilation == null) + { + throw new InvalidOperationException("Failed to get compilation"); + } + + // Find missing handle types + var handleDiscoverer = new MissingHandleTypeDiscoverer(logger, compilation, ct); + var missingHandleTypes = handleDiscoverer.GetMissingHandleTypes(); + + // Generate syntax nodes containing empty structs to represent the missing handle types + var structGenerator = new EmptyStructGenerator(); + var syntaxNodes = structGenerator.GenerateSyntaxNodes(missingHandleTypes); + + // Add syntax nodes to the project as new documents + foreach (var (fullyQualifiedName, node) in syntaxNodes) + { + var relativePath = $"Handles/{PathForFullyQualified(fullyQualifiedName)}"; + project = project + .AddDocument( + Path.GetFileName(relativePath), + node.NormalizeWhitespace(), + filePath: project.FullPath(relativePath) + ) + .Project; + } + + ctx.SourceProject = project; + } + + private class MissingHandleTypeDiscoverer( + ILogger logger, + Compilation compilation, + CancellationToken ct + ) : SymbolVisitor + { + private readonly HashSet _nonHandleTypes = + new(SymbolEqualityComparer.Default); + private readonly Dictionary _missingTypes = + new(SymbolEqualityComparer.Default); + + private string? _currentNamespace = null; + private int _pointerTypeDepth = 0; + + /// + /// Gets all missing handle types that are found and the namespace that they should be created in. + /// + public Dictionary GetMissingHandleTypes() + { + // We need to find and generate all missing handle types + // Handle types are types that are only referenced through a pointer + // We do this by parsing through the list of type errors + var typeErrors = compilation + .GetDiagnostics(ct) + .Where(d => d.Id == "CS0246") // Type errors + .ToList(); + + // Find symbols that contain ITypeErrorSymbols + // These symbols are not necessarily ITypeErrorSymbols + var symbolsFound = new HashSet(SymbolEqualityComparer.Default); + foreach (var typeError in typeErrors) + { + var syntaxTree = typeError.Location.SourceTree; + if (syntaxTree == null) + { + continue; + } + + var semanticModel = compilation.GetSemanticModel(syntaxTree); + + // Get the syntax node the type error corresponds to + var currentSyntax = syntaxTree.GetRoot().FindNode(typeError.Location.SourceSpan); + + // Search upwards to find a syntax node that we can call GetDeclaredSymbol on + // This is because calling GetDeclaredSymbol on the starting node will just return null + var isSuccess = false; + while (currentSyntax != null && currentSyntax is not TypeDeclarationSyntax) + { + switch (currentSyntax) + { + case VariableDeclarationSyntax variableDeclarationSyntax: + { + foreach (var declaratorSyntax in variableDeclarationSyntax.Variables) + { + var symbol = semanticModel.GetDeclaredSymbol(declaratorSyntax, ct); + if (symbol != null) + { + symbolsFound.Add(symbol); + isSuccess = true; + + // All of the declarators will have the same type, so getting the first symbol is enough + break; + } + } + + break; + } + case MemberDeclarationSyntax memberDeclarationSyntax: + { + var symbol = semanticModel.GetDeclaredSymbol( + memberDeclarationSyntax, + ct + ); + if (symbol != null) + { + symbolsFound.Add(symbol); + isSuccess = true; + } + + break; + } + // Skip syntaxes that will never contain handle types + case BaseTypeSyntax: + case AttributeSyntax: + { + isSuccess = true; + break; + } + } + + currentSyntax = currentSyntax.Parent; + } + + if (!isSuccess) + { + // This is to warn of unhandled cases + logger.LogWarning( + "Failed to find corresponding symbol for type error. There may be an unhandled case in the code" + ); + } + } + + // These symbols contain at least one IErrorTypeSymbol, we need to search downwards for them + foreach (var symbol in symbolsFound) + { + Visit(symbol); + } + + return new Dictionary( + _missingTypes.Where(kvp => !_nonHandleTypes.Contains(kvp.Key)), + SymbolEqualityComparer.Default + ); + } + + public override void VisitMethod(IMethodSymbol symbol) + { + base.VisitMethod(symbol); + + _currentNamespace = symbol.NamespaceFromSymbol(); + foreach (var parameterSymbol in symbol.Parameters) + { + Visit(parameterSymbol); + } + _currentNamespace = null; + } + + public override void VisitParameter(IParameterSymbol symbol) + { + base.VisitParameter(symbol); + + _currentNamespace = symbol.NamespaceFromSymbol(); + Visit(symbol.Type); + _currentNamespace = null; + } + + public override void VisitProperty(IPropertySymbol symbol) + { + base.VisitProperty(symbol); + + _currentNamespace = symbol.NamespaceFromSymbol(); + Visit(symbol.Type); + _currentNamespace = null; + } + + public override void VisitField(IFieldSymbol symbol) + { + base.VisitField(symbol); + + _currentNamespace = symbol.NamespaceFromSymbol(); + Visit(symbol.Type); + _currentNamespace = null; + } + + public override void VisitLocal(ILocalSymbol symbol) + { + base.VisitLocal(symbol); + + _currentNamespace = symbol.NamespaceFromSymbol(); + Visit(symbol.Type); + _currentNamespace = null; + } + + public override void VisitPointerType(IPointerTypeSymbol symbol) + { + base.VisitPointerType(symbol); + + _pointerTypeDepth++; + Visit(symbol.PointedAtType); + _pointerTypeDepth--; + } + + public override void VisitNamedType(INamedTypeSymbol symbol) + { + base.VisitNamedType(symbol); + + if (symbol is IErrorTypeSymbol errorTypeSymbol) + { + if (_currentNamespace == null) + { + throw new InvalidOperationException( + $"{nameof(_currentNamespace)} should not be null" + ); + } + + if (_pointerTypeDepth == 0) + { + _nonHandleTypes.Add(errorTypeSymbol); + } + + if (_missingTypes.TryGetValue(errorTypeSymbol, out var sharedNamespace)) + { + _missingTypes[errorTypeSymbol] = NameUtils + .FindCommonPrefix([sharedNamespace, _currentNamespace], true, false, true) + .Trim('.'); + } + else + { + _missingTypes[errorTypeSymbol] = _currentNamespace; + } + } + } + } + + private class EmptyStructGenerator + { + /// + /// Generates a syntax node for each specified type. + /// + /// Map from error type symbol to the namespace the type should be created in. + /// Map from the fully qualified name of the generated type to the syntax node containing code for that type. + public Dictionary GenerateSyntaxNodes( + Dictionary typesToGenerate + ) => + GenerateSyntaxNodes( + typesToGenerate + .Select(kvp => new KeyValuePair(kvp.Key.Name, kvp.Value)) + .ToDictionary() + ); + + /// + /// Generates a syntax node for each specified type. + /// + /// Map from type name to the namespace the type should be created in. + /// Map from the fully qualified name of the generated type to the syntax node containing code for that type. + public Dictionary GenerateSyntaxNodes( + Dictionary missingHandleTypes + ) + { + var results = new Dictionary(); + foreach (var (name, ns) in missingHandleTypes) + { + var fullyQualifiedName = string.IsNullOrWhiteSpace(ns) ? name : $"{ns}.{name}"; + var structDeclarationSyntax = StructDeclaration(name) + .WithModifiers( + TokenList( + Token(SyntaxKind.PublicKeyword), + Token(SyntaxKind.UnsafeKeyword), + Token(SyntaxKind.PartialKeyword) + ) + ); + + results[fullyQualifiedName] = CompilationUnit() + .WithMembers( + SingletonList( + string.IsNullOrWhiteSpace(ns) + ? structDeclarationSyntax + : FileScopedNamespaceDeclaration( + ModUtils.NamespaceIntoIdentifierName(ns) + ) + .WithMembers( + SingletonList( + structDeclarationSyntax + ) + ) + ) + ); + } + + return results; + } + } +} diff --git a/sources/SilkTouch/SilkTouch/Mods/TransformHandles.cs b/sources/SilkTouch/SilkTouch/Mods/TransformHandles.cs index 9f4e8782aa..fb57d9db1f 100644 --- a/sources/SilkTouch/SilkTouch/Mods/TransformHandles.cs +++ b/sources/SilkTouch/SilkTouch/Mods/TransformHandles.cs @@ -25,11 +25,6 @@ namespace Silk.NET.SilkTouch.Mods; /// VkBuffer*, usages of that pointer will be replaced by VkBufferHandle. For a 2-dimensional pointer, /// VkBuffer**, the resulting replacement is VkBufferHandle*. /// -/// -/// It is assumed that all handle types in the generated syntax do not require a using directive in order to be -/// in scope. That is, if a file with the namespace Silk.NET.OpenGL is encountered and it is referencing -/// Program, Program must be declared in Silk.NET.OpenGL, Silk.NET, or Silk. -/// [ModConfiguration] public class TransformHandles( IOptionsSnapshot config, @@ -41,12 +36,6 @@ ILogger logger /// public class Config { - /// - /// Whether it should be assumed that missing types are likely opaque if they are only used as a pointer type - /// and therefore should be subjected to handle transformations. - /// - public bool AssumeMissingTypesOpaque { get; init; } - /// /// Whether the DSL (i.e. nullptr) should be usable with handle types. /// @@ -58,6 +47,7 @@ public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct = { await base.ExecuteAsync(ctx, ct); + var cfg = config.Get(ctx.JobKey); var project = ctx.SourceProject; if (project == null) { @@ -70,38 +60,6 @@ public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct = throw new InvalidOperationException("Failed to get compilation"); } - var cfg = config.Get(ctx.JobKey); - if (cfg.AssumeMissingTypesOpaque) - { - // Find missing handle types - var handleDiscoverer = new MissingHandleTypeDiscoverer(logger, compilation, ct); - var missingHandleTypes = handleDiscoverer.GetMissingHandleTypes(); - - // Generate syntax nodes containing empty structs to represent the missing handle types - var structGenerator = new EmptyStructGenerator(); - var syntaxNodes = structGenerator.GenerateSyntaxNodes(missingHandleTypes); - - // Add syntax nodes to the project as new documents - foreach (var (fullyQualifiedName, node) in syntaxNodes) - { - var relativePath = $"Handles/{PathForFullyQualified(fullyQualifiedName)}"; - project = project - .AddDocument( - Path.GetFileName(relativePath), - node.NormalizeWhitespace(), - filePath: project.FullPath(relativePath) - ) - .Project; - } - - // Update compilation - compilation = await project.GetCompilationAsync(ct); - if (compilation == null) - { - throw new InvalidOperationException("Failed to get compilation"); - } - } - // Find handle documents var handleTypeDiscoverer = new HandleTypeDiscoverer(project, compilation, ct); var handleTypes = await handleTypeDiscoverer.GetHandleTypesAsync(); @@ -167,268 +125,6 @@ [new PointerDimensionReductionTransformer()], ctx.SourceProject = project; } - private class MissingHandleTypeDiscoverer( - ILogger logger, - Compilation compilation, - CancellationToken ct - ) : SymbolVisitor - { - private readonly HashSet _nonHandleTypes = - new(SymbolEqualityComparer.Default); - private readonly Dictionary _missingTypes = - new(SymbolEqualityComparer.Default); - - private string? _currentNamespace = null; - private int _pointerTypeDepth = 0; - - /// - /// Gets all missing handle types that are found and the namespace that they should be created in. - /// - public Dictionary GetMissingHandleTypes() - { - // We need to find and generate all missing handle types - // Handle types are types that are only referenced through a pointer - // We do this by parsing through the list of type errors - var typeErrors = compilation - .GetDiagnostics(ct) - .Where(d => d.Id == "CS0246") // Type errors - .ToList(); - - // Find symbols that contain ITypeErrorSymbols - // These symbols are not necessarily ITypeErrorSymbols - var symbolsFound = new HashSet(SymbolEqualityComparer.Default); - foreach (var typeError in typeErrors) - { - var syntaxTree = typeError.Location.SourceTree; - if (syntaxTree == null) - { - continue; - } - - var semanticModel = compilation.GetSemanticModel(syntaxTree); - - // Get the syntax node the type error corresponds to - var currentSyntax = syntaxTree.GetRoot().FindNode(typeError.Location.SourceSpan); - - // Search upwards to find a syntax node that we can call GetDeclaredSymbol on - // This is because calling GetDeclaredSymbol on the starting node will just return null - var isSuccess = false; - while (currentSyntax != null && currentSyntax is not TypeDeclarationSyntax) - { - switch (currentSyntax) - { - case VariableDeclarationSyntax variableDeclarationSyntax: - { - foreach (var declaratorSyntax in variableDeclarationSyntax.Variables) - { - var symbol = semanticModel.GetDeclaredSymbol(declaratorSyntax, ct); - if (symbol != null) - { - symbolsFound.Add(symbol); - isSuccess = true; - - // All of the declarators will have the same type, so getting the first symbol is enough - break; - } - } - - break; - } - case MemberDeclarationSyntax memberDeclarationSyntax: - { - var symbol = semanticModel.GetDeclaredSymbol( - memberDeclarationSyntax, - ct - ); - if (symbol != null) - { - symbolsFound.Add(symbol); - isSuccess = true; - } - - break; - } - // Skip syntaxes that will never contain handle types - case BaseTypeSyntax: - case AttributeSyntax: - { - isSuccess = true; - break; - } - } - - currentSyntax = currentSyntax.Parent; - } - - if (!isSuccess) - { - // This is to warn of unhandled cases - logger.LogWarning( - "Failed to find corresponding symbol for type error. There may be an unhandled case in the code" - ); - } - } - - // These symbols contain at least one IErrorTypeSymbol, we need to search downwards for them - foreach (var symbol in symbolsFound) - { - Visit(symbol); - } - - return new Dictionary( - _missingTypes.Where(kvp => !_nonHandleTypes.Contains(kvp.Key)), - SymbolEqualityComparer.Default - ); - } - - public override void VisitMethod(IMethodSymbol symbol) - { - base.VisitMethod(symbol); - - _currentNamespace = symbol.NamespaceFromSymbol(); - foreach (var parameterSymbol in symbol.Parameters) - { - Visit(parameterSymbol); - } - _currentNamespace = null; - } - - public override void VisitParameter(IParameterSymbol symbol) - { - base.VisitParameter(symbol); - - _currentNamespace = symbol.NamespaceFromSymbol(); - Visit(symbol.Type); - _currentNamespace = null; - } - - public override void VisitProperty(IPropertySymbol symbol) - { - base.VisitProperty(symbol); - - _currentNamespace = symbol.NamespaceFromSymbol(); - Visit(symbol.Type); - _currentNamespace = null; - } - - public override void VisitField(IFieldSymbol symbol) - { - base.VisitField(symbol); - - _currentNamespace = symbol.NamespaceFromSymbol(); - Visit(symbol.Type); - _currentNamespace = null; - } - - public override void VisitLocal(ILocalSymbol symbol) - { - base.VisitLocal(symbol); - - _currentNamespace = symbol.NamespaceFromSymbol(); - Visit(symbol.Type); - _currentNamespace = null; - } - - public override void VisitPointerType(IPointerTypeSymbol symbol) - { - base.VisitPointerType(symbol); - - _pointerTypeDepth++; - Visit(symbol.PointedAtType); - _pointerTypeDepth--; - } - - public override void VisitNamedType(INamedTypeSymbol symbol) - { - base.VisitNamedType(symbol); - - if (symbol is IErrorTypeSymbol errorTypeSymbol) - { - if (_currentNamespace == null) - { - throw new InvalidOperationException( - $"{nameof(_currentNamespace)} should not be null" - ); - } - - if (_pointerTypeDepth == 0) - { - _nonHandleTypes.Add(errorTypeSymbol); - } - - if (_missingTypes.TryGetValue(errorTypeSymbol, out var sharedNamespace)) - { - _missingTypes[errorTypeSymbol] = NameUtils - .FindCommonPrefix([sharedNamespace, _currentNamespace], true, false, true) - .Trim('.'); - } - else - { - _missingTypes[errorTypeSymbol] = _currentNamespace; - } - } - } - } - - private class EmptyStructGenerator - { - /// - /// Generates a syntax node for each specified type. - /// - /// Map from error type symbol to the namespace the type should be created in. - /// Map from the fully qualified name of the generated type to the syntax node containing code for that type. - public Dictionary GenerateSyntaxNodes( - Dictionary typesToGenerate - ) => - GenerateSyntaxNodes( - typesToGenerate - .Select(kvp => new KeyValuePair(kvp.Key.Name, kvp.Value)) - .ToDictionary() - ); - - /// - /// Generates a syntax node for each specified type. - /// - /// Map from type name to the namespace the type should be created in. - /// Map from the fully qualified name of the generated type to the syntax node containing code for that type. - public Dictionary GenerateSyntaxNodes( - Dictionary missingHandleTypes - ) - { - var results = new Dictionary(); - foreach (var (name, ns) in missingHandleTypes) - { - var fullyQualifiedName = string.IsNullOrWhiteSpace(ns) ? name : $"{ns}.{name}"; - var structDeclarationSyntax = StructDeclaration(name) - .WithModifiers( - TokenList( - Token(SyntaxKind.PublicKeyword), - Token(SyntaxKind.UnsafeKeyword), - Token(SyntaxKind.PartialKeyword) - ) - ); - - results[fullyQualifiedName] = CompilationUnit() - .WithMembers( - SingletonList( - string.IsNullOrWhiteSpace(ns) - ? structDeclarationSyntax - : FileScopedNamespaceDeclaration( - ModUtils.NamespaceIntoIdentifierName(ns) - ) - .WithMembers( - SingletonList( - structDeclarationSyntax - ) - ) - ) - ); - } - - return results; - } - } - private class HandleTypeDiscoverer( Project project, Compilation compilation, From 4b119b292ed23f13ee192ad8b640d38adef02864 Mon Sep 17 00:00:00 2001 From: William Chen Date: Wed, 25 Mar 2026 11:19:10 -0400 Subject: [PATCH 4/9] Rename UseDSL to UseDsl Considering we decided to follow Microsoft's Framework Design Guidelines (acronym threshold of 2) for the bindings and rest of the API, might as well be consistent here. --- generator.json | 6 +++--- sources/SilkTouch/SilkTouch/Mods/TransformHandles.cs | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/generator.json b/generator.json index 2831592107..54df300afb 100644 --- a/generator.json +++ b/generator.json @@ -60,7 +60,7 @@ "InputTestRoot": "tests/SDL" }, "TransformHandles": { - "UseDSL": true + "UseDsl": true }, "TransformFunctions": { "BoolTypes": { @@ -352,7 +352,7 @@ "BenefitOfTheDoubtArrayTransformation": true }, "TransformHandles": { - "UseDSL": true + "UseDsl": true }, "StripAttributes": { "Remove": [ @@ -424,7 +424,7 @@ } }, "TransformHandles": { - "UseDSL": true + "UseDsl": true }, "PrettifyNames": { "GlobalPrefixHints": ["PFN_vk","vk"], diff --git a/sources/SilkTouch/SilkTouch/Mods/TransformHandles.cs b/sources/SilkTouch/SilkTouch/Mods/TransformHandles.cs index fb57d9db1f..9938826448 100644 --- a/sources/SilkTouch/SilkTouch/Mods/TransformHandles.cs +++ b/sources/SilkTouch/SilkTouch/Mods/TransformHandles.cs @@ -39,7 +39,7 @@ public class Config /// /// Whether the DSL (i.e. nullptr) should be usable with handle types. /// - public bool UseDSL { get; init; } + public bool UseDsl { get; init; } } /// @@ -99,7 +99,7 @@ [new PointerDimensionReductionTransformer()], project = ctx.SourceProject; // Use document IDs from earlier - var handleTypeRewriter = new HandleTypeRewriter(cfg.UseDSL); + var handleTypeRewriter = new HandleTypeRewriter(cfg.UseDsl); foreach (var (originalName, documentId) in handleTypeDocumentIds) { var document = @@ -227,7 +227,7 @@ public override void VisitNamedType(INamedTypeSymbol symbol) } } - private class HandleTypeRewriter(bool useDSL) : CSharpSyntaxRewriter + private class HandleTypeRewriter(bool useDsl) : CSharpSyntaxRewriter { public override SyntaxNode VisitStructDeclaration(StructDeclarationSyntax node) { @@ -241,7 +241,7 @@ public override SyntaxNode VisitStructDeclaration(StructDeclarationSyntax node) .WithMembers( List( GetDefaultHandleMembers(structName) - .Concat(useDSL ? GetDSLHandleMembers(structName) : []) + .Concat(useDsl ? GetDslHandleMembers(structName) : []) ) ) .WithModifiers( @@ -465,7 +465,7 @@ string structName .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)); } - private static IEnumerable GetDSLHandleMembers(string structName) + private static IEnumerable GetDslHandleMembers(string structName) { yield return MethodDeclaration( PredefinedType(Token(SyntaxKind.BoolKeyword)), From f3900b93327261f25622b094c6d654b32cea0be2 Mon Sep 17 00:00:00 2001 From: William Chen Date: Wed, 25 Mar 2026 11:21:01 -0400 Subject: [PATCH 5/9] Cleanup unused property in TransformHandles --- sources/SilkTouch/SilkTouch/Mods/TransformHandles.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sources/SilkTouch/SilkTouch/Mods/TransformHandles.cs b/sources/SilkTouch/SilkTouch/Mods/TransformHandles.cs index 9938826448..024215d5e4 100644 --- a/sources/SilkTouch/SilkTouch/Mods/TransformHandles.cs +++ b/sources/SilkTouch/SilkTouch/Mods/TransformHandles.cs @@ -66,7 +66,7 @@ public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct = // Store handle document IDs for later // We will use these IDs to know which documents to rewrite and rename - var handleTypeDocumentIds = new List<(string OriginalName, DocumentId DocumentId)>(); + var handleTypeDocumentIds = new List(); foreach (var handleTypeSymbol in handleTypes) { if (handleTypeSymbol.DeclaringSyntaxReferences.Length > 1) @@ -82,7 +82,7 @@ public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct = var documentId = project.GetDocumentId(declaringSyntaxReference.SyntaxTree); if (documentId != null) { - handleTypeDocumentIds.Add((handleTypeSymbol.Name, documentId)); + handleTypeDocumentIds.Add(documentId); } } @@ -100,7 +100,7 @@ [new PointerDimensionReductionTransformer()], // Use document IDs from earlier var handleTypeRewriter = new HandleTypeRewriter(cfg.UseDsl); - foreach (var (originalName, documentId) in handleTypeDocumentIds) + foreach (var documentId in handleTypeDocumentIds) { var document = project.GetDocument(documentId) From 851c3ff81412a595ec9925d7ca37f2234512e2f2 Mon Sep 17 00:00:00 2001 From: William Chen Date: Wed, 25 Mar 2026 11:21:28 -0400 Subject: [PATCH 6/9] Cleanup usages of redundant collections expressions in TransformHandles --- .../SilkTouch/SilkTouch/Mods/TransformHandles.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sources/SilkTouch/SilkTouch/Mods/TransformHandles.cs b/sources/SilkTouch/SilkTouch/Mods/TransformHandles.cs index 024215d5e4..04feaa8cd0 100644 --- a/sources/SilkTouch/SilkTouch/Mods/TransformHandles.cs +++ b/sources/SilkTouch/SilkTouch/Mods/TransformHandles.cs @@ -323,7 +323,7 @@ string structName Identifier("Equals") ) .WithModifiers( - TokenList([Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.OverrideKeyword)]) + TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.OverrideKeyword)) ) .WithParameterList( ParameterList( @@ -362,7 +362,7 @@ string structName Identifier("GetHashCode") ) .WithModifiers( - TokenList([Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.OverrideKeyword)]) + TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.OverrideKeyword)) ) .WithExpressionBody( ArrowExpressionClause( @@ -394,7 +394,7 @@ string structName Token(SyntaxKind.EqualsEqualsToken) ) .WithModifiers( - TokenList([Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword)]) + TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword)) ) .WithParameterList( ParameterList( @@ -431,7 +431,7 @@ string structName Token(SyntaxKind.ExclamationEqualsToken) ) .WithModifiers( - TokenList([Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword)]) + TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword)) ) .WithParameterList( ParameterList( @@ -494,7 +494,7 @@ private static IEnumerable GetDslHandleMembers(string s Token(SyntaxKind.EqualsEqualsToken) ) .WithModifiers( - TokenList([Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword)]) + TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword)) ) .WithParameterList( ParameterList( @@ -531,7 +531,7 @@ private static IEnumerable GetDslHandleMembers(string s Token(SyntaxKind.ExclamationEqualsToken) ) .WithModifiers( - TokenList([Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword)]) + TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword)) ) .WithParameterList( ParameterList( @@ -569,7 +569,7 @@ private static IEnumerable GetDslHandleMembers(string s IdentifierName(structName) ) .WithModifiers( - TokenList([Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword)]) + TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword)) ) .WithParameterList( ParameterList( From 2f018ff96904e14e07462385f8168c17f3c85399 Mon Sep 17 00:00:00 2001 From: William Chen Date: Fri, 27 Mar 2026 15:08:11 -0400 Subject: [PATCH 7/9] Add NameAffixer.AddResolvedNameAffix() for handling compound names --- .../SilkTouch/SilkTouch/Naming/NameAffixer.cs | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/sources/SilkTouch/SilkTouch/Naming/NameAffixer.cs b/sources/SilkTouch/SilkTouch/Naming/NameAffixer.cs index a778a7d2dc..5a160814f2 100644 --- a/sources/SilkTouch/SilkTouch/Naming/NameAffixer.cs +++ b/sources/SilkTouch/SilkTouch/Naming/NameAffixer.cs @@ -92,17 +92,87 @@ public static SyntaxList AddNameAffix( Literal($"\"{affixType}\"", affixType) ) ); + var categoryArgument = AttributeArgument( LiteralExpression( SyntaxKind.StringLiteralExpression, Literal($"\"{category}\"", category) ) ); + var affixArgument = AttributeArgument( LiteralExpression(SyntaxKind.StringLiteralExpression, Literal($"\"{affix}\"", affix)) ); + var argumentList = AttributeArgumentList([typeArgument, categoryArgument, affixArgument]); + var attribute = AttributeList([Attribute(IdentifierName("NameAffix"), argumentList)]); + + return addToInner ? [attribute, .. attributeLists] : [.. attributeLists, attribute]; + } + + /// + /// This is similar to + /// but allows the name of another symbol to be referenced. + /// + /// This ensures transformations applied to the referenced symbol's name + /// are also applied to this affix. + /// + /// + /// This allows compound names to be handled more cleanly. + /// + /// + /// For example, PerformanceCounterDescriptionARM can be used as a resolved prefix for PerformanceCounterDescriptionARMName. + /// If PerformanceCounterDescriptionARM becomes ARMPerformanceCounterDescription, + /// then PerformanceCounterDescriptionARMName will also be output as ARMPerformanceCounterDescriptionName. + /// + public static SyntaxList AddResolvedNameAffix( + this IEnumerable attributeLists, + NameAffixType type, + string category, + string resolvedAffix, + bool addToInner = false + ) + { + var affixType = type switch + { + NameAffixType.Prefix => "Prefix", + NameAffixType.Suffix => "Suffix", + _ => throw new ArgumentOutOfRangeException(nameof(type)), + }; + + var typeArgument = AttributeArgument( + LiteralExpression( + SyntaxKind.StringLiteralExpression, + Literal($"\"{affixType}\"", affixType) + ) + ); + + var categoryArgument = AttributeArgument( + LiteralExpression( + SyntaxKind.StringLiteralExpression, + Literal($"\"{category}\"", category) + ) + ); + // nameof(resolvedAffix) + var affixArgument = AttributeArgument( + InvocationExpression( + IdentifierName( + Identifier( + TriviaList(), + SyntaxKind.NameOfKeyword, + "nameof", + "nameof", + TriviaList() + ) + ) + ) + .WithArgumentList( + ArgumentList(SingletonSeparatedList(Argument(IdentifierName(resolvedAffix)))) + ) + ); + + var argumentList = AttributeArgumentList([typeArgument, categoryArgument, affixArgument]); var attribute = AttributeList([Attribute(IdentifierName("NameAffix"), argumentList)]); return addToInner ? [attribute, .. attributeLists] : [.. attributeLists, attribute]; From 50a411535b8fb87c3c0bc4baf42e9d7ef6f696a8 Mon Sep 17 00:00:00 2001 From: William Chen Date: Fri, 27 Mar 2026 15:17:38 -0400 Subject: [PATCH 8/9] Use AddResolvedNameAffix for extracted function pointer delegate types --- sources/SilkTouch/SilkTouch/Mods/ExtractNestedTyping.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTyping.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTyping.cs index fd53e3a615..6267ec59b0 100644 --- a/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTyping.cs +++ b/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTyping.cs @@ -529,6 +529,11 @@ _fallbackFromOuterFunctionPointer is not null : default ) .WithNativeName(currentNativeTypeName) + .AddResolvedNameAffix( + NameAffixType.Prefix, + "FunctionPointerParent", + currentNativeTypeName + ) .AddNameAffix( NameAffixType.Suffix, "FunctionPointerDelegateType", From 83888787a396bfa7a3863acb565e619f932fed0e Mon Sep 17 00:00:00 2001 From: William Chen Date: Fri, 27 Mar 2026 15:18:07 -0400 Subject: [PATCH 9/9] Use AddResolvedNameAffix for extracted nested structs and remove name separation hack (no longer necessary) --- .../SilkTouch/Mods/ExtractNestedTyping.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTyping.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTyping.cs index 6267ec59b0..215028fb15 100644 --- a/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTyping.cs +++ b/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTyping.cs @@ -322,11 +322,20 @@ mem is not StructDeclarationSyntax struc continue; } - var iden = $"{node.Identifier}_{match.Groups[1].Value}"; + var iden = $"{node.Identifier}{match.Groups[1].Value}"; _typeRenames[struc.Identifier.ToString()] = iden; struc = - VisitStructDeclaration(struc.WithIdentifier(Identifier(iden))) - as StructDeclarationSyntax + VisitStructDeclaration( + struc + .WithIdentifier(Identifier(iden)) + .WithAttributeLists( + struc.AttributeLists.AddResolvedNameAffix( + NameAffixType.Prefix, + "NestedStructParent", + node.Identifier.ToString() + ) + ) + ) as StructDeclarationSyntax ?? struc; ExtractedNestedStructs.Add(struc); members = members.RemoveAt(i--);