From 2d71ec13434386a9e37cebd495a6f861450ea913 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Mon, 13 Jul 2026 23:13:17 -0700 Subject: [PATCH 1/3] Don't emit function-local type declarations inline in the method body A struct/union defined inline as a local variable's type was emitted verbatim in the middle of the method body, producing invalid C# (dotnet/clangsharp#270). The record is already hoisted to the enclosing type scope by VisitFunctionDecl, so skip the inline type declaration in VisitDeclStmt and let the first value declarator carry the type prefix. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../PInvokeGenerator.VisitStmt.cs | 25 ++++++++++++--- .../PInvokeGenerator.VisitVarDecl.cs | 16 +++++++++- ...edRecordDeclTest.CSharp.Compatible.Unix.cs | 31 +++++++++++++++++++ ...ecordDeclTest.CSharp.Compatible.Windows.cs | 31 +++++++++++++++++++ ...estedRecordDeclTest.CSharp.Default.Unix.cs | 31 +++++++++++++++++++ ...edRecordDeclTest.CSharp.Default.Windows.cs | 31 +++++++++++++++++++ ...NestedRecordDeclTest.CSharp.Latest.Unix.cs | 31 +++++++++++++++++++ ...tedRecordDeclTest.CSharp.Latest.Windows.cs | 31 +++++++++++++++++++ ...estedRecordDeclTest.CSharp.Preview.Unix.cs | 31 +++++++++++++++++++ ...edRecordDeclTest.CSharp.Preview.Windows.cs | 31 +++++++++++++++++++ ...stedRecordDeclTest.Xml.Compatible.Unix.xml | 27 ++++++++++++++++ ...dRecordDeclTest.Xml.Compatible.Windows.xml | 27 ++++++++++++++++ .../NestedRecordDeclTest.Xml.Default.Unix.xml | 27 ++++++++++++++++ ...stedRecordDeclTest.Xml.Default.Windows.xml | 27 ++++++++++++++++ .../NestedRecordDeclTest.Xml.Latest.Unix.xml | 27 ++++++++++++++++ ...estedRecordDeclTest.Xml.Latest.Windows.xml | 27 ++++++++++++++++ .../NestedRecordDeclTest.Xml.Preview.Unix.xml | 27 ++++++++++++++++ ...stedRecordDeclTest.Xml.Preview.Windows.xml | 27 ++++++++++++++++ .../FunctionDeclarationBodyImportTest.cs | 19 ++++++++++++ 19 files changed, 519 insertions(+), 5 deletions(-) create mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Compatible.Unix.cs create mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Compatible.Windows.cs create mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Default.Unix.cs create mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Default.Windows.cs create mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Latest.Unix.cs create mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Latest.Windows.cs create mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Preview.Unix.cs create mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Preview.Windows.cs create mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Compatible.Unix.xml create mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Compatible.Windows.xml create mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Default.Unix.xml create mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Default.Windows.xml create mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Latest.Unix.xml create mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Latest.Windows.xml create mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Preview.Unix.xml create mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Preview.Windows.xml diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs index ccf968aa..11b12819 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs @@ -1116,16 +1116,33 @@ private void VisitDeclStmt(DeclStmt declStmt) { var singleDecl = declStmt.SingleDecl; Debug.Assert(singleDecl is not null); - Visit(singleDecl); + + // A type declared inline (e.g. a struct/union used as a variable's type) has no C# + // equivalent inside a method body; it is hoisted to the enclosing type scope by + // VisitFunctionDecl, so it must not be emitted here. + if (singleDecl is not TypeDecl) + { + Visit(singleDecl); + } } else { - Visit(declStmt.Decls[0]); + var isFirst = true; - foreach (var decl in declStmt.Decls.Skip(1)) + foreach (var decl in declStmt.Decls) { - outputBuilder.Write(", "); + if (decl is TypeDecl) + { + continue; + } + + if (!isFirst) + { + outputBuilder.Write(", "); + } + Visit(decl); + isFirst = false; } } diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitVarDecl.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitVarDecl.cs index f5bc631a..c1cf254b 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitVarDecl.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitVarDecl.cs @@ -295,7 +295,21 @@ void ForDeclStmt(VarDecl varDecl, DeclStmt declStmt) var name = GetRemappedCursorName(varDecl); var escapedName = EscapeName(name); - if (varDecl == declStmt.Decls[0]) + // The type prefix (`T` in `T a, b`) is written by the first value declarator. Inline type + // declarations (a struct/union defined as the variable's type) are skipped when emitting the + // DeclStmt, so key off the first non-type declaration rather than Decls[0]. + var firstValueDecl = null as Decl; + + foreach (var decl in declStmt.Decls) + { + if (decl is not TypeDecl) + { + firstValueDecl = decl; + break; + } + } + + if (varDecl == firstValueDecl) { var type = varDecl.Type; var typeName = GetRemappedTypeName(varDecl, context: null, type, out _); diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Compatible.Unix.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Compatible.Unix.cs new file mode 100644 index 00000000..13301c3c --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Compatible.Unix.cs @@ -0,0 +1,31 @@ +using System.Runtime.InteropServices; + +namespace ClangSharp.Test +{ + public partial struct MyStruct + { + public int x; + } + + public static partial class Methods + { + public static int MyFunction() + { + _Anonymous_e__Union u = new _Anonymous_e__Union(); + + u.i = 0; + return u.s.x; + } + + [StructLayout(LayoutKind.Explicit)] + public partial struct _Anonymous_e__Union + { + [FieldOffset(0)] + [NativeTypeName("struct MyStruct")] + public MyStruct s; + + [FieldOffset(0)] + public int i; + } + } +} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Compatible.Windows.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Compatible.Windows.cs new file mode 100644 index 00000000..13301c3c --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Compatible.Windows.cs @@ -0,0 +1,31 @@ +using System.Runtime.InteropServices; + +namespace ClangSharp.Test +{ + public partial struct MyStruct + { + public int x; + } + + public static partial class Methods + { + public static int MyFunction() + { + _Anonymous_e__Union u = new _Anonymous_e__Union(); + + u.i = 0; + return u.s.x; + } + + [StructLayout(LayoutKind.Explicit)] + public partial struct _Anonymous_e__Union + { + [FieldOffset(0)] + [NativeTypeName("struct MyStruct")] + public MyStruct s; + + [FieldOffset(0)] + public int i; + } + } +} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Default.Unix.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Default.Unix.cs new file mode 100644 index 00000000..13301c3c --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Default.Unix.cs @@ -0,0 +1,31 @@ +using System.Runtime.InteropServices; + +namespace ClangSharp.Test +{ + public partial struct MyStruct + { + public int x; + } + + public static partial class Methods + { + public static int MyFunction() + { + _Anonymous_e__Union u = new _Anonymous_e__Union(); + + u.i = 0; + return u.s.x; + } + + [StructLayout(LayoutKind.Explicit)] + public partial struct _Anonymous_e__Union + { + [FieldOffset(0)] + [NativeTypeName("struct MyStruct")] + public MyStruct s; + + [FieldOffset(0)] + public int i; + } + } +} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Default.Windows.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Default.Windows.cs new file mode 100644 index 00000000..13301c3c --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Default.Windows.cs @@ -0,0 +1,31 @@ +using System.Runtime.InteropServices; + +namespace ClangSharp.Test +{ + public partial struct MyStruct + { + public int x; + } + + public static partial class Methods + { + public static int MyFunction() + { + _Anonymous_e__Union u = new _Anonymous_e__Union(); + + u.i = 0; + return u.s.x; + } + + [StructLayout(LayoutKind.Explicit)] + public partial struct _Anonymous_e__Union + { + [FieldOffset(0)] + [NativeTypeName("struct MyStruct")] + public MyStruct s; + + [FieldOffset(0)] + public int i; + } + } +} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Latest.Unix.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Latest.Unix.cs new file mode 100644 index 00000000..13301c3c --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Latest.Unix.cs @@ -0,0 +1,31 @@ +using System.Runtime.InteropServices; + +namespace ClangSharp.Test +{ + public partial struct MyStruct + { + public int x; + } + + public static partial class Methods + { + public static int MyFunction() + { + _Anonymous_e__Union u = new _Anonymous_e__Union(); + + u.i = 0; + return u.s.x; + } + + [StructLayout(LayoutKind.Explicit)] + public partial struct _Anonymous_e__Union + { + [FieldOffset(0)] + [NativeTypeName("struct MyStruct")] + public MyStruct s; + + [FieldOffset(0)] + public int i; + } + } +} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Latest.Windows.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Latest.Windows.cs new file mode 100644 index 00000000..13301c3c --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Latest.Windows.cs @@ -0,0 +1,31 @@ +using System.Runtime.InteropServices; + +namespace ClangSharp.Test +{ + public partial struct MyStruct + { + public int x; + } + + public static partial class Methods + { + public static int MyFunction() + { + _Anonymous_e__Union u = new _Anonymous_e__Union(); + + u.i = 0; + return u.s.x; + } + + [StructLayout(LayoutKind.Explicit)] + public partial struct _Anonymous_e__Union + { + [FieldOffset(0)] + [NativeTypeName("struct MyStruct")] + public MyStruct s; + + [FieldOffset(0)] + public int i; + } + } +} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Preview.Unix.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Preview.Unix.cs new file mode 100644 index 00000000..13301c3c --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Preview.Unix.cs @@ -0,0 +1,31 @@ +using System.Runtime.InteropServices; + +namespace ClangSharp.Test +{ + public partial struct MyStruct + { + public int x; + } + + public static partial class Methods + { + public static int MyFunction() + { + _Anonymous_e__Union u = new _Anonymous_e__Union(); + + u.i = 0; + return u.s.x; + } + + [StructLayout(LayoutKind.Explicit)] + public partial struct _Anonymous_e__Union + { + [FieldOffset(0)] + [NativeTypeName("struct MyStruct")] + public MyStruct s; + + [FieldOffset(0)] + public int i; + } + } +} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Preview.Windows.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Preview.Windows.cs new file mode 100644 index 00000000..13301c3c --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Preview.Windows.cs @@ -0,0 +1,31 @@ +using System.Runtime.InteropServices; + +namespace ClangSharp.Test +{ + public partial struct MyStruct + { + public int x; + } + + public static partial class Methods + { + public static int MyFunction() + { + _Anonymous_e__Union u = new _Anonymous_e__Union(); + + u.i = 0; + return u.s.x; + } + + [StructLayout(LayoutKind.Explicit)] + public partial struct _Anonymous_e__Union + { + [FieldOffset(0)] + [NativeTypeName("struct MyStruct")] + public MyStruct s; + + [FieldOffset(0)] + public int i; + } + } +} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Compatible.Unix.xml b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Compatible.Unix.xml new file mode 100644 index 00000000..cac24923 --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Compatible.Unix.xml @@ -0,0 +1,27 @@ + + + + + + int + + + + + int + _Anonymous_e__Union u = new _Anonymous_e__Union(); + + u.i = 0; + return u.s.x; + + + + MyStruct + + + int + + + + + diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Compatible.Windows.xml b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Compatible.Windows.xml new file mode 100644 index 00000000..cac24923 --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Compatible.Windows.xml @@ -0,0 +1,27 @@ + + + + + + int + + + + + int + _Anonymous_e__Union u = new _Anonymous_e__Union(); + + u.i = 0; + return u.s.x; + + + + MyStruct + + + int + + + + + diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Default.Unix.xml b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Default.Unix.xml new file mode 100644 index 00000000..cac24923 --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Default.Unix.xml @@ -0,0 +1,27 @@ + + + + + + int + + + + + int + _Anonymous_e__Union u = new _Anonymous_e__Union(); + + u.i = 0; + return u.s.x; + + + + MyStruct + + + int + + + + + diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Default.Windows.xml b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Default.Windows.xml new file mode 100644 index 00000000..cac24923 --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Default.Windows.xml @@ -0,0 +1,27 @@ + + + + + + int + + + + + int + _Anonymous_e__Union u = new _Anonymous_e__Union(); + + u.i = 0; + return u.s.x; + + + + MyStruct + + + int + + + + + diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Latest.Unix.xml b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Latest.Unix.xml new file mode 100644 index 00000000..cac24923 --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Latest.Unix.xml @@ -0,0 +1,27 @@ + + + + + + int + + + + + int + _Anonymous_e__Union u = new _Anonymous_e__Union(); + + u.i = 0; + return u.s.x; + + + + MyStruct + + + int + + + + + diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Latest.Windows.xml b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Latest.Windows.xml new file mode 100644 index 00000000..cac24923 --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Latest.Windows.xml @@ -0,0 +1,27 @@ + + + + + + int + + + + + int + _Anonymous_e__Union u = new _Anonymous_e__Union(); + + u.i = 0; + return u.s.x; + + + + MyStruct + + + int + + + + + diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Preview.Unix.xml b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Preview.Unix.xml new file mode 100644 index 00000000..cac24923 --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Preview.Unix.xml @@ -0,0 +1,27 @@ + + + + + + int + + + + + int + _Anonymous_e__Union u = new _Anonymous_e__Union(); + + u.i = 0; + return u.s.x; + + + + MyStruct + + + int + + + + + diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Preview.Windows.xml b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Preview.Windows.xml new file mode 100644 index 00000000..cac24923 --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Preview.Windows.xml @@ -0,0 +1,27 @@ + + + + + + int + + + + + int + _Anonymous_e__Union u = new _Anonymous_e__Union(); + + u.i = 0; + return u.s.x; + + + + MyStruct + + + int + + + + + diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/FunctionDeclarationBodyImportTest.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/FunctionDeclarationBodyImportTest.cs index b9e29c10..02f0e23d 100644 --- a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/FunctionDeclarationBodyImportTest.cs +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/FunctionDeclarationBodyImportTest.cs @@ -875,6 +875,25 @@ public Task UnaryOperatorPrefixTest(string opcode) return ValidateAsync(nameof(UnaryOperatorPrefixTest), inputContents, discriminator: $"{opcode}"); } + [Test] + public Task NestedRecordDeclTest() + { + var inputContents = @"struct MyStruct +{ + int x; +}; + +int MyFunction() +{ + union { struct MyStruct s; int i; } u; + u.i = 0; + return u.s.x; +} +"; + + return ValidateAsync(nameof(NestedRecordDeclTest), inputContents); + } + [Test] public Task WhileTest() { From d5e66381f9ad307748208b1eec206042da6cc2f7 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Tue, 14 Jul 2026 07:26:37 -0700 Subject: [PATCH 2/3] Unify identical baselines to reduce duplication The added NestedRecordDeclTest baseline is identical across every (config, os) variant, so collapse it to a single mode-level baseline via the harness fallback chain instead of checking in 16 duplicate files. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- ...ecordDeclTest.CSharp.Compatible.Windows.cs | 31 ------------------- ...estedRecordDeclTest.CSharp.Default.Unix.cs | 31 ------------------- ...edRecordDeclTest.CSharp.Default.Windows.cs | 31 ------------------- ...NestedRecordDeclTest.CSharp.Latest.Unix.cs | 31 ------------------- ...tedRecordDeclTest.CSharp.Latest.Windows.cs | 31 ------------------- ...estedRecordDeclTest.CSharp.Preview.Unix.cs | 31 ------------------- ...edRecordDeclTest.CSharp.Preview.Windows.cs | 31 ------------------- ...Unix.cs => NestedRecordDeclTest.CSharp.cs} | 0 ...dRecordDeclTest.Xml.Compatible.Windows.xml | 27 ---------------- .../NestedRecordDeclTest.Xml.Default.Unix.xml | 27 ---------------- ...stedRecordDeclTest.Xml.Default.Windows.xml | 27 ---------------- .../NestedRecordDeclTest.Xml.Latest.Unix.xml | 27 ---------------- ...estedRecordDeclTest.Xml.Latest.Windows.xml | 27 ---------------- .../NestedRecordDeclTest.Xml.Preview.Unix.xml | 27 ---------------- ...stedRecordDeclTest.Xml.Preview.Windows.xml | 27 ---------------- ....Unix.xml => NestedRecordDeclTest.Xml.xml} | 0 16 files changed, 406 deletions(-) delete mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Compatible.Windows.cs delete mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Default.Unix.cs delete mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Default.Windows.cs delete mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Latest.Unix.cs delete mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Latest.Windows.cs delete mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Preview.Unix.cs delete mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Preview.Windows.cs rename tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/{NestedRecordDeclTest.CSharp.Compatible.Unix.cs => NestedRecordDeclTest.CSharp.cs} (100%) delete mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Compatible.Windows.xml delete mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Default.Unix.xml delete mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Default.Windows.xml delete mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Latest.Unix.xml delete mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Latest.Windows.xml delete mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Preview.Unix.xml delete mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Preview.Windows.xml rename tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/{NestedRecordDeclTest.Xml.Compatible.Unix.xml => NestedRecordDeclTest.Xml.xml} (100%) diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Compatible.Windows.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Compatible.Windows.cs deleted file mode 100644 index 13301c3c..00000000 --- a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Compatible.Windows.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Runtime.InteropServices; - -namespace ClangSharp.Test -{ - public partial struct MyStruct - { - public int x; - } - - public static partial class Methods - { - public static int MyFunction() - { - _Anonymous_e__Union u = new _Anonymous_e__Union(); - - u.i = 0; - return u.s.x; - } - - [StructLayout(LayoutKind.Explicit)] - public partial struct _Anonymous_e__Union - { - [FieldOffset(0)] - [NativeTypeName("struct MyStruct")] - public MyStruct s; - - [FieldOffset(0)] - public int i; - } - } -} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Default.Unix.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Default.Unix.cs deleted file mode 100644 index 13301c3c..00000000 --- a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Default.Unix.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Runtime.InteropServices; - -namespace ClangSharp.Test -{ - public partial struct MyStruct - { - public int x; - } - - public static partial class Methods - { - public static int MyFunction() - { - _Anonymous_e__Union u = new _Anonymous_e__Union(); - - u.i = 0; - return u.s.x; - } - - [StructLayout(LayoutKind.Explicit)] - public partial struct _Anonymous_e__Union - { - [FieldOffset(0)] - [NativeTypeName("struct MyStruct")] - public MyStruct s; - - [FieldOffset(0)] - public int i; - } - } -} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Default.Windows.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Default.Windows.cs deleted file mode 100644 index 13301c3c..00000000 --- a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Default.Windows.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Runtime.InteropServices; - -namespace ClangSharp.Test -{ - public partial struct MyStruct - { - public int x; - } - - public static partial class Methods - { - public static int MyFunction() - { - _Anonymous_e__Union u = new _Anonymous_e__Union(); - - u.i = 0; - return u.s.x; - } - - [StructLayout(LayoutKind.Explicit)] - public partial struct _Anonymous_e__Union - { - [FieldOffset(0)] - [NativeTypeName("struct MyStruct")] - public MyStruct s; - - [FieldOffset(0)] - public int i; - } - } -} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Latest.Unix.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Latest.Unix.cs deleted file mode 100644 index 13301c3c..00000000 --- a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Latest.Unix.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Runtime.InteropServices; - -namespace ClangSharp.Test -{ - public partial struct MyStruct - { - public int x; - } - - public static partial class Methods - { - public static int MyFunction() - { - _Anonymous_e__Union u = new _Anonymous_e__Union(); - - u.i = 0; - return u.s.x; - } - - [StructLayout(LayoutKind.Explicit)] - public partial struct _Anonymous_e__Union - { - [FieldOffset(0)] - [NativeTypeName("struct MyStruct")] - public MyStruct s; - - [FieldOffset(0)] - public int i; - } - } -} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Latest.Windows.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Latest.Windows.cs deleted file mode 100644 index 13301c3c..00000000 --- a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Latest.Windows.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Runtime.InteropServices; - -namespace ClangSharp.Test -{ - public partial struct MyStruct - { - public int x; - } - - public static partial class Methods - { - public static int MyFunction() - { - _Anonymous_e__Union u = new _Anonymous_e__Union(); - - u.i = 0; - return u.s.x; - } - - [StructLayout(LayoutKind.Explicit)] - public partial struct _Anonymous_e__Union - { - [FieldOffset(0)] - [NativeTypeName("struct MyStruct")] - public MyStruct s; - - [FieldOffset(0)] - public int i; - } - } -} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Preview.Unix.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Preview.Unix.cs deleted file mode 100644 index 13301c3c..00000000 --- a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Preview.Unix.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Runtime.InteropServices; - -namespace ClangSharp.Test -{ - public partial struct MyStruct - { - public int x; - } - - public static partial class Methods - { - public static int MyFunction() - { - _Anonymous_e__Union u = new _Anonymous_e__Union(); - - u.i = 0; - return u.s.x; - } - - [StructLayout(LayoutKind.Explicit)] - public partial struct _Anonymous_e__Union - { - [FieldOffset(0)] - [NativeTypeName("struct MyStruct")] - public MyStruct s; - - [FieldOffset(0)] - public int i; - } - } -} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Preview.Windows.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Preview.Windows.cs deleted file mode 100644 index 13301c3c..00000000 --- a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Preview.Windows.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Runtime.InteropServices; - -namespace ClangSharp.Test -{ - public partial struct MyStruct - { - public int x; - } - - public static partial class Methods - { - public static int MyFunction() - { - _Anonymous_e__Union u = new _Anonymous_e__Union(); - - u.i = 0; - return u.s.x; - } - - [StructLayout(LayoutKind.Explicit)] - public partial struct _Anonymous_e__Union - { - [FieldOffset(0)] - [NativeTypeName("struct MyStruct")] - public MyStruct s; - - [FieldOffset(0)] - public int i; - } - } -} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Compatible.Unix.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.cs similarity index 100% rename from tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.Compatible.Unix.cs rename to tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.cs diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Compatible.Windows.xml b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Compatible.Windows.xml deleted file mode 100644 index cac24923..00000000 --- a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Compatible.Windows.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - int - - - - - int - _Anonymous_e__Union u = new _Anonymous_e__Union(); - - u.i = 0; - return u.s.x; - - - - MyStruct - - - int - - - - - diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Default.Unix.xml b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Default.Unix.xml deleted file mode 100644 index cac24923..00000000 --- a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Default.Unix.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - int - - - - - int - _Anonymous_e__Union u = new _Anonymous_e__Union(); - - u.i = 0; - return u.s.x; - - - - MyStruct - - - int - - - - - diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Default.Windows.xml b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Default.Windows.xml deleted file mode 100644 index cac24923..00000000 --- a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Default.Windows.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - int - - - - - int - _Anonymous_e__Union u = new _Anonymous_e__Union(); - - u.i = 0; - return u.s.x; - - - - MyStruct - - - int - - - - - diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Latest.Unix.xml b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Latest.Unix.xml deleted file mode 100644 index cac24923..00000000 --- a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Latest.Unix.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - int - - - - - int - _Anonymous_e__Union u = new _Anonymous_e__Union(); - - u.i = 0; - return u.s.x; - - - - MyStruct - - - int - - - - - diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Latest.Windows.xml b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Latest.Windows.xml deleted file mode 100644 index cac24923..00000000 --- a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Latest.Windows.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - int - - - - - int - _Anonymous_e__Union u = new _Anonymous_e__Union(); - - u.i = 0; - return u.s.x; - - - - MyStruct - - - int - - - - - diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Preview.Unix.xml b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Preview.Unix.xml deleted file mode 100644 index cac24923..00000000 --- a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Preview.Unix.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - int - - - - - int - _Anonymous_e__Union u = new _Anonymous_e__Union(); - - u.i = 0; - return u.s.x; - - - - MyStruct - - - int - - - - - diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Preview.Windows.xml b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Preview.Windows.xml deleted file mode 100644 index cac24923..00000000 --- a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Preview.Windows.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - int - - - - - int - _Anonymous_e__Union u = new _Anonymous_e__Union(); - - u.i = 0; - return u.s.x; - - - - MyStruct - - - int - - - - - diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Compatible.Unix.xml b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.xml similarity index 100% rename from tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.Compatible.Unix.xml rename to tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.xml From 027929986ebb8d748bc256edcd26dc2a521c4dee Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Tue, 14 Jul 2026 07:38:43 -0700 Subject: [PATCH 3/3] Qualify hoisted function-local anonymous records by their function Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../PInvokeGenerator.Naming.cs | 46 ++++++++++++++++++- .../NestedRecordDeclCollisionTest.CSharp.cs | 43 +++++++++++++++++ .../NestedRecordDeclCollisionTest.Xml.xml | 37 +++++++++++++++ .../NestedRecordDeclTest.CSharp.cs | 4 +- .../NestedRecordDeclTest.Xml.xml | 4 +- .../FunctionDeclarationBodyImportTest.cs | 21 +++++++++ 6 files changed, 150 insertions(+), 5 deletions(-) create mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclCollisionTest.CSharp.cs create mode 100644 tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclCollisionTest.Xml.xml diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.Naming.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.Naming.cs index 018e9798..3f74cd2b 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.Naming.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.Naming.cs @@ -682,10 +682,54 @@ private string GetRemappedNameForAnonymousRecord(RecordDecl recordDecl) } else { - return $"_Anonymous{AnonymousTypeKindTag}{(recordDecl.IsUnion ? "Union" : "Struct")}"; + return GetRemappedNameForFunctionLocalAnonymousRecord(recordDecl); } } + private string GetRemappedNameForFunctionLocalAnonymousRecord(RecordDecl recordDecl) + { + var kind = recordDecl.IsUnion ? "Union" : "Struct"; + + // A record declared in a function body is hoisted out to the enclosing type, so qualify it + // with the enclosing function's name; otherwise two functions that each declare an anonymous + // record would both hoist to `_Anonymous_e__Union` and clash. + var functionDecl = null as FunctionDecl; + + for (var declContext = recordDecl.DeclContext; declContext is Decl decl; declContext = decl.DeclContext) + { + if (decl is FunctionDecl candidate) + { + functionDecl = candidate; + break; + } + } + + if (functionDecl is null) + { + return $"_Anonymous{AnonymousTypeKindTag}{kind}"; + } + + var remappedNameBuilder = new StringBuilder(); + _ = remappedNameBuilder.Append('_'); + _ = remappedNameBuilder.Append(GetRemappedCursorName(functionDecl)); + + // Multiple anonymous records of the same kind in one function would still collide on the + // function-qualified name, so disambiguate them by their order within the function. + var sameKindRecords = functionDecl.Decls + .OfType() + .Where((other) => (other.IsUnion == recordDecl.IsUnion) && IsAnonymousRecord(GetCursorName(other))) + .ToList(); + + if (sameKindRecords.Count > 1) + { + _ = remappedNameBuilder.Append(sameKindRecords.IndexOf(recordDecl) + 1); + } + + _ = remappedNameBuilder.Append(AnonymousTypeKindTag); + _ = remappedNameBuilder.Append(kind); + return remappedNameBuilder.ToString(); + } + private string GetRemappedName(string name, Cursor? cursor, bool tryRemapOperatorName, out bool wasRemapped, bool skipUsing = false) => GetRemappedName(name, cursor, tryRemapOperatorName, out wasRemapped, skipUsing, skipUsingIfNotRemapped: skipUsing); diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclCollisionTest.CSharp.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclCollisionTest.CSharp.cs new file mode 100644 index 00000000..39a1f8b5 --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclCollisionTest.CSharp.cs @@ -0,0 +1,43 @@ +using System.Runtime.InteropServices; + +namespace ClangSharp.Test +{ + public static partial class Methods + { + public static int MyFunction1() + { + _MyFunction1_e__Union u = new _MyFunction1_e__Union(); + + u.a = 0; + return u.a; + } + + [StructLayout(LayoutKind.Explicit)] + public partial struct _MyFunction1_e__Union + { + [FieldOffset(0)] + public int a; + + [FieldOffset(0)] + public float b; + } + + public static int MyFunction2() + { + _MyFunction2_e__Union u = new _MyFunction2_e__Union(); + + u.a = 0; + return u.a; + } + + [StructLayout(LayoutKind.Explicit)] + public partial struct _MyFunction2_e__Union + { + [FieldOffset(0)] + public int a; + + [FieldOffset(0)] + public float b; + } + } +} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclCollisionTest.Xml.xml b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclCollisionTest.Xml.xml new file mode 100644 index 00000000..abd81f19 --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclCollisionTest.Xml.xml @@ -0,0 +1,37 @@ + + + + + + int + _MyFunction1_e__Union u = new _MyFunction1_e__Union(); + + u.a = 0; + return u.a; + + + + int + + + float + + + + int + _MyFunction2_e__Union u = new _MyFunction2_e__Union(); + + u.a = 0; + return u.a; + + + + int + + + float + + + + + diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.cs index 13301c3c..0d18add2 100644 --- a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.cs +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.CSharp.cs @@ -11,14 +11,14 @@ public static partial class Methods { public static int MyFunction() { - _Anonymous_e__Union u = new _Anonymous_e__Union(); + _MyFunction_e__Union u = new _MyFunction_e__Union(); u.i = 0; return u.s.x; } [StructLayout(LayoutKind.Explicit)] - public partial struct _Anonymous_e__Union + public partial struct _MyFunction_e__Union { [FieldOffset(0)] [NativeTypeName("struct MyStruct")] diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.xml b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.xml index cac24923..3f096b0d 100644 --- a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.xml +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationBodyImport/NestedRecordDeclTest.Xml.xml @@ -9,12 +9,12 @@ int - _Anonymous_e__Union u = new _Anonymous_e__Union(); + _MyFunction_e__Union u = new _MyFunction_e__Union(); u.i = 0; return u.s.x; - + MyStruct diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/FunctionDeclarationBodyImportTest.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/FunctionDeclarationBodyImportTest.cs index ce70d99d..88081875 100644 --- a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/FunctionDeclarationBodyImportTest.cs +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/FunctionDeclarationBodyImportTest.cs @@ -918,6 +918,27 @@ int TestMethod() { return ValidateAsync(nameof(ThisAsPointerTest), inputContents); } + [Test] + public Task NestedRecordDeclCollisionTest() + { + var inputContents = @"int MyFunction1() +{ + union { int a; float b; } u; + u.a = 0; + return u.a; +} + +int MyFunction2() +{ + union { int a; float b; } u; + u.a = 0; + return u.a; +} +"; + + return ValidateAsync(nameof(NestedRecordDeclCollisionTest), inputContents); + } + [Test] public Task WhileTest() {