diff --git a/generator.json b/generator.json index 8bbae05cda..54df300afb 100644 --- a/generator.json +++ b/generator.json @@ -42,6 +42,7 @@ "AddIncludes", "ClangScraper", "MarkNativeNames", + "ExtractHandles", "ExtractNestedTyping", "TransformHandles", "TransformFunctions", @@ -59,8 +60,7 @@ "InputTestRoot": "tests/SDL" }, "TransformHandles": { - "AssumeMissingTypesOpaque": true, - "UseDSL": true + "UseDsl": true }, "TransformFunctions": { "BoolTypes": { @@ -228,6 +228,7 @@ "ChangeNativeClass", "AddApiProfiles", "MixKhronosData", + "ExtractHandles", "ExtractNestedTyping", "TransformHandles", "InterceptNativeFunctions", @@ -351,8 +352,7 @@ "BenefitOfTheDoubtArrayTransformation": true }, "TransformHandles": { - "AssumeMissingTypesOpaque": true, - "UseDSL": true + "UseDsl": true }, "StripAttributes": { "Remove": [ @@ -370,6 +370,7 @@ "AddIncludes", "ClangScraper", "MarkNativeNames", + "ExtractHandles", "ExtractNestedTyping", "TransformHandles", "MixKhronosData", @@ -423,8 +424,7 @@ } }, "TransformHandles": { - "AssumeMissingTypesOpaque": true, - "UseDSL": true + "UseDsl": true }, "PrettifyNames": { "GlobalPrefixHints": ["PFN_vk","vk"], 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/ExtractNestedTyping.cs b/sources/SilkTouch/SilkTouch/Mods/ExtractNestedTyping.cs index fd53e3a615..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--); @@ -529,6 +538,11 @@ _fallbackFromOuterFunctionPointer is not null : default ) .WithNativeName(currentNativeTypeName) + .AddResolvedNameAffix( + NameAffixType.Prefix, + "FunctionPointerParent", + currentNativeTypeName + ) .AddNameAffix( NameAffixType.Suffix, "FunctionPointerDelegateType", diff --git a/sources/SilkTouch/SilkTouch/Mods/TransformHandles.cs b/sources/SilkTouch/SilkTouch/Mods/TransformHandles.cs index 5e958705c6..04feaa8cd0 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,16 +36,10 @@ 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. /// - public bool UseDSL { get; init; } + public bool UseDsl { get; init; } } /// @@ -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,45 +60,13 @@ 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(); // 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) @@ -124,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); } } @@ -141,8 +99,8 @@ [new PointerDimensionReductionTransformer()], project = ctx.SourceProject; // Use document IDs from earlier - var handleTypeRewriter = new HandleTypeRewriter(cfg.UseDSL); - foreach (var (originalName, documentId) in handleTypeDocumentIds) + var handleTypeRewriter = new HandleTypeRewriter(cfg.UseDsl); + foreach (var documentId in handleTypeDocumentIds) { var document = project.GetDocument(documentId) @@ -165,270 +123,6 @@ [new PointerDimensionReductionTransformer()], } ctx.SourceProject = project; - - return; - } - - 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( @@ -533,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) { @@ -547,7 +241,7 @@ public override SyntaxNode VisitStructDeclaration(StructDeclarationSyntax node) .WithMembers( List( GetDefaultHandleMembers(structName) - .Concat(useDSL ? GetDSLHandleMembers(structName) : []) + .Concat(useDsl ? GetDslHandleMembers(structName) : []) ) ) .WithModifiers( @@ -629,7 +323,7 @@ string structName Identifier("Equals") ) .WithModifiers( - TokenList([Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.OverrideKeyword)]) + TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.OverrideKeyword)) ) .WithParameterList( ParameterList( @@ -668,7 +362,7 @@ string structName Identifier("GetHashCode") ) .WithModifiers( - TokenList([Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.OverrideKeyword)]) + TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.OverrideKeyword)) ) .WithExpressionBody( ArrowExpressionClause( @@ -700,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( @@ -737,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( @@ -771,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)), @@ -800,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( @@ -837,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( @@ -875,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( 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];