Browse Source

Extend UseLambdaSyntax to emit statement lambdas

Under UseLambdaSyntax, anonymous functions became lambdas only when an
expression body was possible; statement-bodied ones kept C# 2 delegate
syntax. Now every anonymous function whose parameter shape a lambda can
express uses lambda syntax; delegate syntax remains for ref/out/in and
params parameters and for pre-C# 3 language profiles.

Two latent issues surfaced by the wider lambda coverage: DeclareVariables
assumed an insertion point directly under a LambdaExpression is an
expression body it must convert to a block, which block-bodied lambdas
now violate; and anonymous methods declared without a parameter list
carry compiler-generated parameter names like '<p0>' that are not valid
identifiers, so the lambda's mandatory parameter list regenerates such
names from the parameter type: (object obj, EventArgs e) => ...
A side effect visible in fixtures: an explicit parameter list can make
a delegate-creation cast redundant that bare 'delegate' syntax needed
for overload resolution, e.g. new Thread((ThreadStart)delegate { })
becomes new Thread(() => { }).

Assisted-by: Claude:claude-fable-5:Claude Code
pull/3975/head
Siegfried Pammer 1 month ago committed by Siegfried Pammer
parent
commit
3cc49044ec
  1. 6
      ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs
  2. 22
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/AnonymousMethodEdgeCases.cs
  3. 79
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/AnonymousMethodEdgeCases.il
  4. 2
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1038.cs
  5. 2
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs
  6. 2
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomTaskType.cs
  7. 2
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/DeconstructionTests.cs
  8. 68
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs
  9. 6
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.cs
  10. 6
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/FixProxyCalls.cs
  11. 6
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/InitializerTests.cs
  12. 4
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3439.cs
  13. 2
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3751.cs
  14. 26
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs
  15. 2
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/OutVariables.cs
  16. 6
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/PropertiesAndEvents.cs
  17. 2
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/QualifierTests.cs
  18. 6
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTests.cs
  19. 2
      ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.Expected.cs
  20. 5
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  21. 15
      ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs
  22. 8
      ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs

6
ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs

@ -51,6 +51,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -51,6 +51,12 @@ namespace ICSharpCode.Decompiler.Tests
}
}
[Test]
public async Task AnonymousMethodEdgeCases()
{
await Run();
}
[Test, Ignore("Need to decide how to represent virtual methods without 'newslot' flag")]
public async Task Issue379()
{

22
ICSharpCode.Decompiler.Tests/TestCases/ILPretty/AnonymousMethodEdgeCases.cs

@ -0,0 +1,22 @@ @@ -0,0 +1,22 @@
using System;
namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty
{
public class AnonymousMethodEdgeCases
{
public Func<int, int> AssignmentIsTheLambdaBody()
{
return (int x) => {
int num;
return num = x;
};
}
public Action<object> UsedParameterWithInvalidName()
{
return (object value) => {
Console.WriteLine(value);
};
}
}
}

79
ICSharpCode.Decompiler.Tests/TestCases/ILPretty/AnonymousMethodEdgeCases.il

@ -0,0 +1,79 @@ @@ -0,0 +1,79 @@
// Hand-written input for two anonymous-method shapes that C# cannot express directly:
// an expression-bodied lambda whose body is the assignment declaring its own local, and
// a parameter whose metadata name is not a valid C# identifier but IS used in the body.
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 )
.ver 4:0:0:0
}
.assembly extern System.Core
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 )
.ver 4:0:0:0
}
.assembly AnonymousMethodEdgeCases
{
.ver 1:0:0:0
}
.module AnonymousMethodEdgeCases.dll
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003
.corflags 0x00000001
.class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.ILPretty.AnonymousMethodEdgeCases
extends [mscorlib]System.Object
{
.method public hidebysig specialname rtspecialname instance void .ctor () cil managed
{
.maxstack 8
ldarg.0
call instance void [mscorlib]System.Object::.ctor()
ret
}
.method public hidebysig instance class [System.Core]System.Func`2<int32, int32> AssignmentIsTheLambdaBody () cil managed
{
.maxstack 8
ldarg.0
ldftn instance int32 ICSharpCode.Decompiler.Tests.TestCases.ILPretty.AnonymousMethodEdgeCases::'<AssignmentIsTheLambdaBody>b__1_0'(int32)
newobj instance void class [System.Core]System.Func`2<int32, int32>::.ctor(object, native int)
ret
}
// return num = x; -- the store's value is the return value, so the local has a store and
// no load, and the decompiled lambda body is the assignment itself.
.method private hidebysig instance int32 '<AssignmentIsTheLambdaBody>b__1_0' (int32 x) cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
.maxstack 2
.locals init (
[0] int32 num
)
ldarg.1
dup
stloc.0
ret
}
.method public hidebysig instance class [System.Core]System.Action`1<object> UsedParameterWithInvalidName () cil managed
{
.maxstack 8
ldarg.0
ldftn instance void ICSharpCode.Decompiler.Tests.TestCases.ILPretty.AnonymousMethodEdgeCases::'<UsedParameterWithInvalidName>b__2_0'(object)
newobj instance void class [System.Core]System.Action`1<object>::.ctor(object, native int)
ret
}
// The parameter name '<p0>' is what C# emits for an anonymous method declared without a
// parameter list; used here, so the lambda's parameter list has to name it somehow.
.method private hidebysig instance void '<UsedParameterWithInvalidName>b__2_0' (object '<p0>') cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
.maxstack 8
ldarg.1
call void [mscorlib]System.Console::WriteLine(object)
ret
}
}

2
ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1038.cs

@ -4,7 +4,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty @@ -4,7 +4,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty
{
public class Issue1038<TK, TR> where TR : class, new()
{
public event Action<TK, TR> TestEvent = delegate {
public event Action<TK, TR> TestEvent = (TK A_0, TR A_1) => {
};
}
}

2
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs

@ -524,7 +524,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -524,7 +524,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static Func<Task<int>> AsyncDelegate()
{
return async delegate {
return async () => {
await Task.Delay(10);
return 2;
};

2
ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomTaskType.cs

@ -119,7 +119,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -119,7 +119,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static Func<ValueTask<int>> AsyncDelegate()
{
return async delegate {
return async () => {
await Task.Delay(10);
return 2;
};

2
ICSharpCode.Decompiler.Tests/TestCases/Pretty/DeconstructionTests.cs

@ -990,7 +990,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -990,7 +990,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
int a = 0;
int b = 0;
await Task.Run(delegate {
await Task.Run(() => {
(a, b) = GetTuple<int, int>();
});
return a + b;

68
ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs

@ -58,14 +58,14 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction @@ -58,14 +58,14 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction
public Action CaptureOfThis()
{
return delegate {
return () => {
CaptureOfThis();
};
}
public Action CaptureOfThisAndParameter(int a)
{
return delegate {
return () => {
CaptureOfThisAndParameter(a);
};
}
@ -76,7 +76,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction @@ -76,7 +76,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction
{
if (item > 0)
{
return delegate {
return () => {
CaptureOfThisAndParameter(item + a);
};
}
@ -91,7 +91,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction @@ -91,7 +91,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction
int copyOfItem = item;
if (item > 0)
{
return delegate {
return () => {
CaptureOfThisAndParameter(item + a + copyOfItem);
};
}
@ -118,18 +118,18 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction @@ -118,18 +118,18 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction
private void Bug955()
{
new Thread((ThreadStart)delegate {
new Thread(() => {
});
}
public void Bug951(int amount)
{
DoAction(delegate {
DoAction(() => {
if (amount < 0)
{
amount = 0;
}
DoAction(delegate {
DoAction(() => {
NoOp(amount);
});
});
@ -138,12 +138,12 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction @@ -138,12 +138,12 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction
public void Bug951b()
{
int amount = Foo();
DoAction(delegate {
DoAction(() => {
if (amount < 0)
{
amount = 0;
}
DoAction(delegate {
DoAction(() => {
NoOp(amount);
});
});
@ -151,8 +151,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction @@ -151,8 +151,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction
public void Bug951c(SomeData data)
{
DoAction(delegate {
DoAction(delegate {
DoAction(() => {
DoAction(() => {
DoSomething(data.Value);
});
});
@ -165,7 +165,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction @@ -165,7 +165,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction
public Action<object> Bug971_DelegateWithoutParameterList()
{
return delegate {
return (object obj) => {
};
}
@ -256,7 +256,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction @@ -256,7 +256,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction
public Func<TCaptured> GetFunc(Func<TNonCaptured, TCaptured> f)
{
TCaptured captured = f(default(TNonCaptured));
return delegate {
return () => {
Console.WriteLine(captured.GetType().FullName);
return captured;
};
@ -265,7 +265,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction @@ -265,7 +265,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction
public Func<TNonCaptured, TNonCapturedMP, TCaptured> GetFunc<TNonCapturedMP>(Func<TCaptured> f)
{
TCaptured captured = f();
return delegate (TNonCaptured a, TNonCapturedMP d) {
return (TNonCaptured a, TNonCapturedMP d) => {
Console.WriteLine(a.GetHashCode());
Console.WriteLine(captured.GetType().FullName);
return captured;
@ -343,7 +343,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction @@ -343,7 +343,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction
for (int i = 0; i < 10; i++)
{
int counter;
list.Add(delegate (int x) {
list.Add((int x) => {
counter = x;
});
}
@ -356,7 +356,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction @@ -356,7 +356,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction
int counter;
for (int i = 0; i < 10; i++)
{
list.Add(delegate (int x) {
list.Add((int x) => {
counter = x;
});
}
@ -365,7 +365,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction @@ -365,7 +365,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction
public static Action StaticAnonymousMethodNoClosure()
{
return delegate {
return () => {
Console.WriteLine();
};
}
@ -383,7 +383,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction @@ -383,7 +383,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction
int j;
for (j = 0; j < 10; j++)
{
list.Add(delegate (int k) {
list.Add((int k) => {
for (int l = 0; l < j; l += k)
{
Console.WriteLine();
@ -398,7 +398,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction @@ -398,7 +398,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction
List<Action<int>> list = new List<Action<int>>();
for (int i = 0; i < 10; i++)
{
list.Add(delegate (int k) {
list.Add((int k) => {
Console.WriteLine(k);
});
}
@ -406,7 +406,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction @@ -406,7 +406,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction
public static Action<int> NameConflict3(int i)
{
return delegate (int j) {
return (int j) => {
for (int k = 0; k < j; k++)
{
Console.WriteLine(k);
@ -427,7 +427,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction @@ -427,7 +427,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction
public static Func<TCaptured> CapturedTypeParameter1<TNonCaptured, TCaptured>(TNonCaptured a, Func<TNonCaptured, TCaptured> f)
{
TCaptured captured = f(a);
return delegate {
return () => {
Console.WriteLine(captured.GetType().FullName);
return captured;
};
@ -436,7 +436,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction @@ -436,7 +436,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction
public static Func<TCaptured> CapturedTypeParameter2<TNonCaptured, TCaptured>(TNonCaptured a, Func<TNonCaptured, List<TCaptured>> f)
{
List<TCaptured> captured = f(a);
return delegate {
return () => {
Console.WriteLine(captured.GetType().FullName);
return captured.FirstOrDefault();
};
@ -624,14 +624,14 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction @@ -624,14 +624,14 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction
{
public void M()
{
Run(delegate (object o) {
Run((object o) => {
try
{
List<int> list = o as List<int>;
Action action = delegate {
Action action = () => {
list.Select((int x) => x * 2);
};
Action action2 = delegate {
Action action2 = () => {
list.Select((int x) => x * 2);
};
Console.WriteLine();
@ -655,6 +655,20 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction @@ -655,6 +655,20 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction
del(x);
}
public void AnonymousMethodWithByRefParameters()
{
RefAction refAction = delegate (ref int reference) {
reference++;
};
OutAction outAction = delegate (out int reference) {
reference = 1;
};
int value = 0;
refAction(ref value);
outAction(out value);
Console.WriteLine(value);
}
public void Issue1572(DelegateConstruction.Dummy dum)
{
#if EXPECTED_OUTPUT
@ -675,4 +689,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction @@ -675,4 +689,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.DelegateConstruction
internal class MyAttribute : Attribute
{
}
public delegate void OutAction(out int value);
public delegate void RefAction(ref int value);
}

6
ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.cs

@ -818,15 +818,15 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -818,15 +818,15 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
Test<Func<int, string>>((int a) => a.ToString(), (int a) => a.ToString());
Test<Func<string, char[]>>((string a) => a.ToArray(), (string a) => a.ToArray());
Test<Func<bool>>(() => 'a'.CompareTo('b') < 0, () => 'a'.CompareTo('b') < 0);
Test<Action<object, bool>>(delegate (object lockObj, bool lockTaken) {
Test<Action<object, bool>>((object lockObj, bool lockTaken) => {
Monitor.Enter(lockObj, ref lockTaken);
}, (object lockObj, bool lockTaken) => Monitor.Enter(lockObj, ref lockTaken));
Test<Func<string, int, bool>>((string str, int num) => int.TryParse(str, out num), (string str, int num) => int.TryParse(str, out num));
Test<Func<string, SimpleType, bool>>((string str, SimpleType t) => int.TryParse(str, out t.Field), (string str, SimpleType t) => int.TryParse(str, out t.Field));
Test<Action<object>>(delegate (object o) {
Test<Action<object>>((object o) => {
TestCall(o);
}, (object o) => TestCall(o));
Test<Action<object>>(delegate (object o) {
Test<Action<object>>((object o) => {
TestCall(ref o);
}, (object o) => TestCall(ref o));
}

6
ICSharpCode.Decompiler.Tests/TestCases/Pretty/FixProxyCalls.cs

@ -96,7 +96,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty @@ -96,7 +96,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty
protected internal override void Test(string test)
{
action = delegate (string a) {
action = (string a) => {
base.Test(a);
};
if (test.Equals(1))
@ -119,7 +119,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty @@ -119,7 +119,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty
{
public Action<object> M(object state)
{
return delegate (object x) {
return (object x) => {
base.BaseCall(x, state, () => (object)null);
};
}
@ -136,7 +136,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty @@ -136,7 +136,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty
{
protected internal override void Test(int a)
{
Action action = delegate {
Action action = () => {
base.Test(a);
};
if (a.Equals(1))

6
ICSharpCode.Decompiler.Tests/TestCases/Pretty/InitializerTests.cs

@ -916,7 +916,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.InitializerTests @@ -916,7 +916,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.InitializerTests
public static void NotAnObjectInitializerWithEvent()
{
Data data = new Data();
data.TestEvent += delegate {
#if NET50
data.TestEvent += (object? obj, EventArgs e) => {
#else
data.TestEvent += (object obj, EventArgs e) => {
#endif
Console.WriteLine();
};
X(Y(), data);

4
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3439.cs

@ -12,7 +12,7 @@ internal class VariableScopeTest @@ -12,7 +12,7 @@ internal class VariableScopeTest
private void Test(List<string> list1)
{
AddAction(delegate (List<Item> list2) {
AddAction((List<Item> list2) => {
long num2 = 1L;
foreach (string item in list1)
{
@ -28,7 +28,7 @@ internal class VariableScopeTest @@ -28,7 +28,7 @@ internal class VariableScopeTest
{
int preservedName = num;
num++;
AddAction(item2, delegate (object x) {
AddAction(item2, (object x) => {
SetValue(x, preservedName);
});
}

2
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3751.cs

@ -13,7 +13,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -13,7 +13,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public object Trigger()
{
return Infer(delegate {
return Infer(() => {
if (Cond)
{
Console.WriteLine();

26
ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs

@ -329,7 +329,7 @@ namespace LocalFunctions @@ -329,7 +329,7 @@ namespace LocalFunctions
private int field;
private Lazy<object> nonCapturinglocalFunctionInLambda = new Lazy<object>(delegate {
private Lazy<object> nonCapturinglocalFunctionInLambda = new Lazy<object>(() => {
return CreateValue();
#if CS80
@ -342,7 +342,7 @@ namespace LocalFunctions @@ -342,7 +342,7 @@ namespace LocalFunctions
}
});
private Lazy<object> capturinglocalFunctionInLambda = new Lazy<object>(delegate {
private Lazy<object> capturinglocalFunctionInLambda = new Lazy<object>(() => {
int x = 42;
return Do();
@ -648,7 +648,7 @@ namespace LocalFunctions @@ -648,7 +648,7 @@ namespace LocalFunctions
public static int LocalFunctionInLambda(IEnumerable<int> xs)
{
return xs.First(delegate (int x) {
return xs.First((int x) => {
return Do();
bool Do()
@ -797,20 +797,20 @@ namespace LocalFunctions @@ -797,20 +797,20 @@ namespace LocalFunctions
{
t0 = 0;
int t2 = t0;
return ((Func<int>)delegate {
return ((Func<int>)(() => {
t0 = 0;
t2 = 0;
return ZZZ2();
})();
}))();
}
int ZZZ2()
{
t0 = 0;
int t3 = t0;
#if !OPT
Func<int> func = delegate {
Func<int> func = () => {
#else
return ((Func<int>)delegate {
return ((Func<int>)(() => {
#endif
t0 = 0;
t3 = 0;
@ -819,7 +819,7 @@ namespace LocalFunctions @@ -819,7 +819,7 @@ namespace LocalFunctions
};
return func();
#else
})();
}))();
#endif
}
}
@ -840,20 +840,20 @@ namespace LocalFunctions @@ -840,20 +840,20 @@ namespace LocalFunctions
{
t0 = 0;
int t2 = t0;
return ((Func<int>)delegate {
return ((Func<int>)(() => {
t0 = 0;
t2 = 0;
return ZZZ2();
})();
}))();
}
int ZZZ2()
{
t0 = 0;
int t3 = t0;
#if !OPT
Func<int> func = delegate {
Func<int> func = () => {
#else
return ((Func<int>)delegate {
return ((Func<int>)(() => {
#endif
t0 = 0;
t3 = 0;
@ -862,7 +862,7 @@ namespace LocalFunctions @@ -862,7 +862,7 @@ namespace LocalFunctions
};
return func();
#else
})();
}))();
#endif
}
}

2
ICSharpCode.Decompiler.Tests/TestCases/Pretty/OutVariables.cs

@ -37,7 +37,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -37,7 +37,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
// to ensure that the value is initialized when the delegate is declared.
if (d.Count > 2 && d.TryGetValue(42, out var value))
{
return delegate {
return () => {
Console.WriteLine(value);
};
}

6
ICSharpCode.Decompiler.Tests/TestCases/Pretty/PropertiesAndEvents.cs

@ -169,7 +169,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -169,7 +169,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public event EventHandler AutomaticEvent;
[field: NonSerialized]
public event EventHandler AutomaticEventWithInitializer = delegate {
#if NET50
public event EventHandler AutomaticEventWithInitializer = (object? obj, EventArgs e) => {
#else
public event EventHandler AutomaticEventWithInitializer = (object obj, EventArgs e) => {
#endif
};
#if ROSLYN

2
ICSharpCode.Decompiler.Tests/TestCases/Pretty/QualifierTests.cs

@ -280,7 +280,7 @@ namespace ICSharpCode.Decompiler.Tests.Pretty @@ -280,7 +280,7 @@ namespace ICSharpCode.Decompiler.Tests.Pretty
{
int fieldConflict = 5;
Capturer(() => this.fieldConflict + fieldConflict);
Capturer(delegate {
Capturer(() => {
int innerConflict = 5;
return this.fieldConflict + fieldConflict + Capturer2(() => this.innerConflict + innerConflict + this.fieldConflict + fieldConflict);
});

6
ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTests.cs

@ -100,14 +100,14 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -100,14 +100,14 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public (int, int) AccessRest => (1, 2, 3, 4, 5, 6, 7, 8, 9).Rest;
public (string, object, Action) TargetTyping => (null, 1, delegate {
public (string, object, Action) TargetTyping => (null, 1, () => {
#pragma warning disable format
});
#pragma warning restore format
public object NotTargetTyping => ((string)null, (object)1, (Action)delegate {
public object NotTargetTyping => ((string)null, (object)1, (Action)(() => {
#pragma warning disable format
});
}));
#pragma warning restore format
public void UnnamedTupleOut(out (int, string, Action, dynamic) tuple)

2
ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.Expected.cs

@ -207,7 +207,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly @@ -207,7 +207,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly
field1 = 1,
field2 = "Hello World!"
};
Invoke(delegate {
Invoke(() => {
displayClass.thisField = new Program();
});
Console.WriteLine("{0} {1}", this, displayClass.thisField);

5
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -2550,8 +2550,9 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2550,8 +2550,9 @@ namespace ICSharpCode.Decompiler.CSharp
}
else if (settings.UseLambdaSyntax && ame.Parameters.All(p => p.ParameterModifier == ReferenceKind.None && !p.IsParams))
{
// otherwise use lambda only if an expression lambda is possible
isLambda = (body.Statements.Count == 1 && body.Statements.Single() is ReturnStatement);
// Lambdas cover statement bodies too; anonymous method syntax remains only for
// parameter shapes a lambda cannot express (ref/out/in and params modifiers).
isLambda = true;
}
// Remove the parameter list from an AnonymousMethodExpression if the parameters are not used in the method body
var parameterReferencingIdentifiers =

15
ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs

@ -472,8 +472,11 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -472,8 +472,11 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
{
// We can only insert variable declarations in blocks, but FindInsertionPoints() didn't
// guarantee that it finds only blocks.
// Fix that up now.
while (!(v.InsertionPoint.nextNode.Parent is BlockStatement or LambdaExpression))
// Fix that up now. A lambda is a valid stop only for its expression body (insertion
// will convert that body to a block); a point at a statement-bodied lambda's block
// itself must keep moving up into the enclosing scope.
while (!(v.InsertionPoint.nextNode.Parent is BlockStatement
|| (v.InsertionPoint.nextNode.Parent is LambdaExpression && v.InsertionPoint.nextNode is Expression)))
{
if (v.InsertionPoint.nextNode.Parent is ForStatement f && v.InsertionPoint.nextNode == f.Initializers.FirstOrDefault() && IsMatchingAssignment(v, out _))
{
@ -593,6 +596,14 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -593,6 +596,14 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
if (v.Type.IsByRefLike)
return true; // by-ref-like variables always must be initialized at their declaration.
if (v.InsertionPoint.nextNode.Parent is LambdaExpression)
{
// The insertion point is an expression-bodied lambda's body. Combining would put a
// declaration statement in expression position ("x => int num = x;"); the separate
// declaration path turns the body into a block first, which stays valid C#.
return false;
}
if (v.InsertionPoint.nextNode.Slot?.Kind == Slots.ForInitializer)
return true; // for-statement initializers always should combine declaration and initialization.

8
ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs

@ -234,6 +234,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -234,6 +234,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (variables.TryGetValue(i, out var v))
variableMapping[v] = name;
}
else if (!IsValidName(name))
{
// Compiler-generated parameter names (e.g. "<p0>" on an anonymous method
// declared without a parameter list) are not valid C# identifiers. Skipping
// the reservation and the mapping leaves the parameter to AssignName, which
// generates a fresh name from the type for any name that fails IsValidName.
continue;
}
string nameWithoutNumber = SplitName(name, out int newIndex);
if (!parentScope.IsReservedVariableName(nameWithoutNumber, out _))
{

Loading…
Cancel
Save