diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.Naming.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.Naming.cs
index ace6923f5..425da93fc 100644
--- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.Naming.cs
+++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.Naming.cs
@@ -793,6 +793,14 @@ private string GetRemappedTypeName(Cursor? cursor, Cursor? context, Type type, o
WithType(enumDecl, ref remappedName, ref nativeTypeName);
}
+ else if (cursor is FieldDecl fieldDecl)
+ {
+ // A field can have its emitted type overridden via `--with-type Struct.field=Type`.
+ // The `*` catch-all is intentionally not honored here, as rewriting every field to
+ // a single type is meaningless; the qualified `Struct.field` key must be explicit.
+
+ WithType(fieldDecl, ref remappedName, ref nativeTypeName, matchStar: false);
+ }
}
}
diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs
index 427ac30e1..9bbe8b72f 100644
--- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs
+++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs
@@ -2399,9 +2399,9 @@ private void WithTestAssertTrue(string actual)
}
}
- private void WithType(NamedDecl namedDecl, ref string integerTypeName, ref string nativeTypeName)
+ private void WithType(NamedDecl namedDecl, ref string integerTypeName, ref string nativeTypeName, bool matchStar = true)
{
- if (TryGetRemappedValue(namedDecl, _config._withTypes, out var type, matchStar: true))
+ if (TryGetRemappedValue(namedDecl, _config._withTypes, out var type, matchStar))
{
if (string.IsNullOrWhiteSpace(nativeTypeName))
{
diff --git a/sources/ClangSharpPInvokeGenerator/Program.Options.cs b/sources/ClangSharpPInvokeGenerator/Program.Options.cs
index 404b7d5e9..1d41e0e6d 100644
--- a/sources/ClangSharpPInvokeGenerator/Program.Options.cs
+++ b/sources/ClangSharpPInvokeGenerator/Program.Options.cs
@@ -84,7 +84,7 @@ internal static partial class Program
private static readonly CommandLineOption s_withSetLastErrors = Multi(s_withSetLastErrorOptionAliases, "Add the SetLastError=true modifier or SetsSystemLastError attribute to a given DllImport or UnmanagedFunctionPointer. Supports wildcards.");
private static readonly CommandLineOption s_withSuppressGCTransitions = Multi(s_withSuppressGCTransitionOptionAliases, "Add the SuppressGCTransition calling convention to a given DllImport or UnmanagedFunctionPointer. Supports wildcards.");
private static readonly CommandLineOption s_withTransparentStructNameValuePairs = Multi(s_withTransparentStructOptionAliases, "A remapped type name to be treated as a transparent wrapper during binding generation. Supports wildcards.");
- private static readonly CommandLineOption s_withTypeNameValuePairs = Multi(s_withTypeOptionAliases, "A type to be used for the given enum declaration during binding generation. Supports wildcards.");
+ private static readonly CommandLineOption s_withTypeNameValuePairs = Multi(s_withTypeOptionAliases, "A type to be used for the given enum declaration, macro constant, or struct field (using the qualified `Type.field`) during binding generation. Supports wildcards.");
private static readonly CommandLineOption s_withUsingNameValuePairs = Multi(s_withUsingOptionAliases, "A using directive to be included for the given remapped declaration name during binding generation. Supports wildcards.");
private static readonly CommandLineOption s_helpOption = Flag(s_helpOptionAliases, "Show help and usage information");
diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/WithTypeFieldTest.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/WithTypeFieldTest.cs
new file mode 100644
index 000000000..870b2cebb
--- /dev/null
+++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/WithTypeFieldTest.cs
@@ -0,0 +1,47 @@
+// Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information.
+
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using NUnit.Framework;
+
+namespace ClangSharp.UnitTests;
+
+///
+/// Regression tests for https://github.com/dotnet/ClangSharp/issues/559.
+/// The --with-type option is extended to accept a field-qualified key (Struct.field=Type)
+/// so the emitted type of an individual struct field can be overridden. The original Clang type is
+/// preserved as a [NativeTypeName] attribute and unrelated fields are left untouched.
+///
+[Platform("win")]
+public sealed class WithTypeFieldTest : PInvokeGeneratorTest
+{
+ [Test]
+ public Task OverridesFieldType()
+ {
+ var inputContents = @"struct Something
+{
+ int type;
+ int other;
+};
+";
+
+ var expectedOutputContents = @"namespace ClangSharp.Test
+{
+ public partial struct Something
+ {
+ [NativeTypeName(""int"")]
+ public SomethingType type;
+
+ public int other;
+ }
+}
+";
+
+ var withTypes = new Dictionary
+ {
+ ["Something.type"] = "SomethingType",
+ };
+
+ return ValidateGeneratedCSharpLatestWindowsBindingsAsync(inputContents, expectedOutputContents, withTypes: withTypes);
+ }
+}