diff --git a/ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs b/ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs
index e5f200486..2239f26aa 100644
--- a/ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs
+++ b/ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs
@@ -295,12 +295,6 @@ namespace ICSharpCode.Decompiler.Tests
RunCS(options: options);
}
- [Test]
- public void LocalFunctions([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions options)
- {
- RunCS(options: options);
- }
-
void RunCS([CallerMemberName] string testName = null, CompilerOptions options = CompilerOptions.UseDebug)
{
string testFileName = testName + ".cs";
diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
index 51ed72689..68a1c2613 100644
--- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
+++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
@@ -83,7 +83,7 @@
-
+
diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
index e7786ac67..fa1bb5dd8 100644
--- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
+++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
@@ -184,6 +184,12 @@ namespace ICSharpCode.Decompiler.Tests
});
}
+ [Test]
+ public void LocalFunctions([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions)
+ {
+ RunForLibrary(cscOptions: cscOptions);
+ }
+
[Test]
public void PropertiesAndEvents([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions)
{
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/LocalFunctions.cs b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/LocalFunctions.cs
deleted file mode 100644
index f05d95f27..000000000
--- a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/LocalFunctions.cs
+++ /dev/null
@@ -1,76 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace LocalFunctions
-{
- class LocalFunctions
- {
- int field;
-
- public static void Main(string[] args)
- {
- StaticContextNoCapture(10);
- StaticContextSimpleCapture(10);
- StaticContextCaptureInForLoop(10);
- var inst = new LocalFunctions() { field = 10 };
- inst.ContextNoCapture();
- inst.ContextSimpleCapture();
- inst.ContextCaptureInForLoop();
- }
-
- public static void StaticContextNoCapture(int length)
- {
- for (int i = 0; i < length; i++) {
- LocalWrite("Hello " + i);
- }
-
- void LocalWrite(string s) => Console.WriteLine(s);
- }
-
- public static void StaticContextSimpleCapture(int length)
- {
- for (int i = 0; i < length; i++) {
- LocalWrite();
- }
-
- void LocalWrite() => Console.WriteLine("Hello " + length);
- }
-
- public static void StaticContextCaptureInForLoop(int length)
- {
- for (int i = 0; i < length; i++) {
- void LocalWrite() => Console.WriteLine("Hello " + i + "/" + length);
- LocalWrite();
- }
- }
-
- public void ContextNoCapture()
- {
- for (int i = 0; i < field; i++) {
- LocalWrite("Hello " + i);
- }
-
- void LocalWrite(string s) => Console.WriteLine(s);
- }
-
- public void ContextSimpleCapture()
- {
- for (int i = 0; i < field; i++) {
- LocalWrite();
- }
-
- void LocalWrite() => Console.WriteLine("Hello " + field);
- }
-
- public void ContextCaptureInForLoop()
- {
- for (int i = 0; i < field; i++) {
- void LocalWrite() => Console.WriteLine("Hello " + i + "/" + field);
- LocalWrite();
- }
- }
- }
-}
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs
new file mode 100644
index 000000000..e06d9a892
--- /dev/null
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs
@@ -0,0 +1,226 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace LocalFunctions
+{
+ internal class LocalFunctions
+ {
+ private int field;
+
+ private static void Test(int x)
+ {
+ }
+
+ private static int GetInt(string a)
+ {
+ return a.Length;
+ }
+
+ private static string GetString(int a)
+ {
+ return a.ToString();
+ }
+
+ public static void StaticContextNoCapture(int length)
+ {
+ for (int i = 0; i < length; i++) {
+ LocalWrite("Hello " + i);
+ }
+
+ void LocalWrite(string s)
+ {
+ Console.WriteLine(s);
+ }
+ }
+
+ public static void StaticContextSimpleCapture(int length)
+ {
+ for (int i = 0; i < length; i++) {
+ LocalWrite();
+ }
+
+ void LocalWrite()
+ {
+ Console.WriteLine("Hello " + length);
+ }
+ }
+
+ public static void StaticContextCaptureForLoopVariable(int length)
+ {
+ int i;
+ for (i = 0; i < length; i++) {
+ LocalWrite();
+ }
+ void LocalWrite()
+ {
+ Console.WriteLine("Hello " + i + "/" + length);
+ }
+ }
+
+ public void ContextNoCapture()
+ {
+ for (int i = 0; i < field; i++) {
+ LocalWrite("Hello " + i);
+ }
+
+ void LocalWrite(string s)
+ {
+ Console.WriteLine(s);
+ }
+ }
+
+ public void ContextSimpleCapture()
+ {
+ for (int i = 0; i < field; i++) {
+ LocalWrite();
+ }
+
+ void LocalWrite()
+ {
+ Console.WriteLine("Hello " + field);
+ }
+ }
+
+ public void ContextCaptureForLoopVariable()
+ {
+ int i;
+ for (i = 0; i < field; i++) {
+ LocalWrite();
+ }
+ void LocalWrite()
+ {
+ Console.WriteLine("Hello " + i + "/" + field);
+ }
+ }
+
+ public void CapturedOutsideLoop()
+ {
+ int i = 0;
+ while (i < field) {
+ i = GetInt("asdf");
+ LocalWrite();
+ }
+
+ void LocalWrite()
+ {
+ Console.WriteLine("Hello " + i + "/" + field);
+ }
+ }
+
+ public void CapturedInForeachLoop(IEnumerable args)
+ {
+ foreach (string arg2 in args) {
+ string arg = arg2;
+ LocalWrite();
+ void LocalWrite()
+ {
+ Console.WriteLine("Hello " + arg);
+ }
+ }
+ }
+
+ public void Overloading()
+ {
+ Test(5);
+ LocalFunctions.Test(2);
+
+ void Test(int x)
+ {
+ Console.WriteLine("x: {0}", x);
+ }
+ }
+
+ public void NamedArgument()
+ {
+ Use(Get(1), Get(2), Get(3));
+ Use(Get(1), c: Get(2), b: Get(3));
+
+ int Get(int i)
+ {
+ return i;
+ }
+
+ void Use(int a, int b, int c)
+ {
+ Console.WriteLine(a + b + c);
+ }
+ }
+
+ public static Func LambdaInLocalFunction()
+ {
+ int x = (int)Math.Pow(2.0, 10.0);
+ Enumerable.Range(1, 100).Select((Func)Transform);
+ return Create();
+
+ Func Create()
+ {
+ return () => x;
+ }
+
+ int Transform(int y)
+ {
+ return 2 * y;
+ }
+ }
+
+ public static Func MethodRef()
+ {
+ int x = (int)Math.Pow(2.0, 10.0);
+ Enumerable.Range(1, 100).Select((Func)F);
+ return null;
+
+ int F(int y)
+ {
+ return x * y;
+ }
+ }
+
+ public static int Fib(int i)
+ {
+ return FibHelper(i);
+
+ int FibHelper(int n)
+ {
+ if (n <= 0) {
+ return 0;
+ }
+
+ return FibHelper(n - 1) + FibHelper(n - 2);
+ }
+ }
+
+ public static int NestedLocalFunctions(int i)
+ {
+ return A();
+
+ int A()
+ {
+ double x = Math.Pow(10.0, 2.0);
+ return B();
+
+ int B()
+ {
+ return i + (int)x;
+ }
+ }
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
index 73b07c70b..bd72ac66d 100644
--- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
+++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
@@ -952,7 +952,7 @@ namespace ICSharpCode.Decompiler.CSharp
void DeclareLocalFunctions(ILFunction currentFunction, BlockContainer container, BlockStatement blockStatement)
{
- foreach (var localFunction in currentFunction.LocalFunctions) {
+ foreach (var localFunction in currentFunction.LocalFunctions.OrderBy(f => f.Name)) {
if (localFunction.DeclarationScope != container)
continue;
blockStatement.Add(TranslateFunction(localFunction));
diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs
index eb2d39d65..38020dc3b 100644
--- a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs
+++ b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs
@@ -263,14 +263,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (inst.Value.MatchLdLoc(out var closureVariable) && displayClasses.TryGetValue(closureVariable, out var displayClass)) {
displayClasses[inst.Variable] = displayClass;
instructionsToRemove.Add(inst);
+ } else if (inst.Variable.Kind == VariableKind.Local && inst.Variable.IsSingleDefinition && inst.Variable.LoadCount == 0 && inst.Value is StLoc) {
+ inst.ReplaceWith(inst.Value);
}
}
- protected internal override void VisitLdLoc(LdLoc inst)
- {
- base.VisitLdLoc(inst);
- }
-
protected internal override void VisitStObj(StObj inst)
{
base.VisitStObj(inst);