diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index 60823cac8..f19374fd5 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -135,6 +135,7 @@ <Compile Include="TestCases\ILPretty\Issue3442.cs" /> <Compile Include="TestCases\ILPretty\MonoFixed.cs" /> <Compile Include="TestCases\Pretty\Comparisons.cs" /> + <Compile Include="TestCases\Pretty\Issue3406.cs" /> <Compile Include="TestCases\Pretty\Issue3439.cs" /> <Compile Include="TestCases\Pretty\Issue3442.cs" /> <None Include="TestCases\VBPretty\VBAutomaticEvents.vb" /> diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs index 178e15a9d..ae6860798 100644 --- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs @@ -610,6 +610,12 @@ namespace ICSharpCode.Decompiler.Tests await RunForLibrary(cscOptions: cscOptions); } + [Test] + public async Task Issue3406([ValueSource(nameof(roslyn4OrNewerOptions))] CompilerOptions cscOptions) + { + await RunForLibrary(cscOptions: cscOptions); + } + [Test] public async Task Issue3442([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3406.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3406.cs new file mode 100644 index 000000000..38220a3e9 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3406.cs @@ -0,0 +1,35 @@ +internal class Issue3406 +{ + private record struct S1(int Value); + + private record struct S2 + { + public int Value; + + public S2(int value) + { + Value = value; + } + + public S2(int a, int b) + { + Value = a + b; + } + } + + private record struct S3 + { + public int Value; + + public S3(int value) + { + Value = value; + } + } + + // This also generates a hidden backing field + private record struct S4(int value) + { + public int Value = value; + } +} \ No newline at end of file diff --git a/ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs b/ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs index 1ddc179b3..10f20da1c 100644 --- a/ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs @@ -222,10 +222,14 @@ namespace ICSharpCode.Decompiler.CSharp { backingMember = property; } - else + else if (!recordTypeDef.IsRecord) { backingMember = field; } + else + { + return false; + } primaryCtorParameterToAutoPropertyOrBackingField.Add(unspecializedMethod.Parameters[i], backingMember); autoPropertyOrBackingFieldToPrimaryCtorParameter.Add(backingMember, unspecializedMethod.Parameters[i]); diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/TransformFieldAndConstructorInitializers.cs b/ICSharpCode.Decompiler/CSharp/Transforms/TransformFieldAndConstructorInitializers.cs index bce3b1071..d1254b244 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/TransformFieldAndConstructorInitializers.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/TransformFieldAndConstructorInitializers.cs @@ -285,9 +285,9 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms case IProperty p: return record.IsPropertyDeclaredByPrimaryConstructor(p); case IField f: - return true; + return record.PrimaryConstructor != null; case IEvent e: - return true; + return record.PrimaryConstructor != null; default: return false; }