From 1d964bce8ed6f56e1e27cf279cff4c4fca61e33a Mon Sep 17 00:00:00 2001
From: ds5678 <49847914+ds5678@users.noreply.github.com>
Date: Thu, 11 Sep 2025 09:35:53 -0700
Subject: [PATCH] Fix regression in decompiling local functions with default
parameters
---
.../ICSharpCode.Decompiler.Tests.csproj | 1 +
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs | 14 ++++++++++++++
.../TestCases/Pretty/Issue3541.cs | 15 +++++++++++++++
.../IL/Transforms/LocalFunctionDecompiler.cs | 7 ++++++-
.../TypeSystem/TypeSystemExtensions.cs | 4 ++++
5 files changed, 40 insertions(+), 1 deletion(-)
create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3541.cs
diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
index 971979b70..d34cd96d3 100644
--- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
+++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
@@ -154,6 +154,7 @@
+
diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
index f34b96b5b..562beb8f2 100644
--- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
+++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
@@ -133,6 +133,14 @@ namespace ICSharpCode.Decompiler.Tests
CompilerOptions.Optimize | CompilerOptions.UseRoslynLatest,
};
+ static readonly CompilerOptions[] roslyn4OrNewerWithNet40Options =
+ {
+ CompilerOptions.UseRoslynLatest | CompilerOptions.TargetNet40,
+ CompilerOptions.Optimize | CompilerOptions.UseRoslynLatest | CompilerOptions.TargetNet40,
+ CompilerOptions.UseRoslynLatest,
+ CompilerOptions.Optimize | CompilerOptions.UseRoslynLatest,
+ };
+
static readonly CompilerOptions[] roslyn4OrNewerOptions =
{
CompilerOptions.UseRoslynLatest,
@@ -664,6 +672,12 @@ namespace ICSharpCode.Decompiler.Tests
await RunForLibrary(cscOptions: cscOptions | CompilerOptions.CheckForOverflowUnderflow, configureDecompiler: settings => settings.CheckForOverflowUnderflow = true);
}
+ [Test]
+ public async Task Issue3541([ValueSource(nameof(roslyn4OrNewerWithNet40Options))] CompilerOptions cscOptions)
+ {
+ await RunForLibrary(cscOptions: cscOptions);
+ }
+
[Test]
public async Task AssemblyCustomAttributes([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions)
{
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3541.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3541.cs
new file mode 100644
index 000000000..c1df03b60
--- /dev/null
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3541.cs
@@ -0,0 +1,15 @@
+namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
+{
+ internal class Issue3541
+ {
+ private void Test(string format)
+ {
+ TestLocal();
+
+ void TestLocal(int a = 0)
+ {
+ a.ToString(format);
+ }
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs
index a5d01d8f0..1d93b677a 100644
--- a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs
+++ b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs
@@ -566,13 +566,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
internal static bool IsClosureParameter(IParameter parameter, ITypeResolveContext context)
+ {
+ return IsClosureParameter(parameter, context.CurrentTypeDefinition);
+ }
+
+ internal static bool IsClosureParameter(IParameter parameter, ITypeDefinition currentTypeDefinition)
{
if (parameter.Type is not ByReferenceType brt)
return false;
var type = brt.ElementType.GetDefinition();
return type != null
&& type.Kind == TypeKind.Struct
- && TransformDisplayClassUsage.IsPotentialClosure(context.CurrentTypeDefinition, type);
+ && TransformDisplayClassUsage.IsPotentialClosure(currentTypeDefinition, type);
}
LocalFunctionMethod ReduceToLocalFunction(IMethod method, int typeParametersToRemove)
diff --git a/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs b/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs
index 1a4b11501..b89e2d10b 100644
--- a/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs
+++ b/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs
@@ -21,6 +21,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Reflection.Metadata;
+using ICSharpCode.Decompiler.IL.Transforms;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Semantics;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
@@ -681,6 +682,9 @@ namespace ICSharpCode.Decompiler.TypeSystem
if (otherParameter == parameter)
break;
+ if (LocalFunctionDecompiler.IsClosureParameter(otherParameter, otherParameter.Owner.DeclaringTypeDefinition))
+ continue;
+
if (DefaultValueAssignmentAllowedIndividual(otherParameter) || otherParameter.IsParams)
continue;