From ef47a2451159af9eb95febfa69d55a55a2ee09c7 Mon Sep 17 00:00:00 2001 From: upstream Date: Sat, 10 Oct 2026 13:18:39 +0200 Subject: [PATCH] Upstream icsharpcode/ILSpy state of the touched files before b3c37fd8d4 --- .../TestCases/Pretty/AsyncAwaitPatterns.cs | 15 +- .../TestCases/Pretty/InitializerTests.cs | 1396 +++++++++++++++++ .../TestCases/Pretty/Records.cs | 532 +++++++ .../IL/Transforms/ILInlining.cs | 4 - 4 files changed, 1931 insertions(+), 16 deletions(-) create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/Pretty/InitializerTests.cs create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/Pretty/Records.cs diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/AsyncAwaitPatterns.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/AsyncAwaitPatterns.cs index d547959..858f8e6 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/AsyncAwaitPatterns.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/AsyncAwaitPatterns.cs @@ -16,8 +16,9 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -// An await shape that still decompiles to code that does not compile is tracked as #4019 (with -// expression); it is absent here because it has no correct output to pin yet. +// Await shapes that still decompile to code that does not compile are tracked as #4017 (type +// parameter with an interface constraint), #4018 (static dynamic call) and #4019 (with +// expression); they are absent here because they have no correct output to pin yet. #pragma warning disable 1998 using System; @@ -538,16 +539,6 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.AsyncAwait await awaitable; } - public async Task TypeParameterWithInterfaceConstraint(T awaitable) where T : IAwaitable - { - await awaitable; - } - - public async Task TypeParameterArrayElementWithInterfaceConstraint(T[] awaitables) where T : IAwaitable - { - await awaitables[0]; - } - public async Task ConfiguredTaskAwaitable(Task task) { #if ROSLYN2 diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InitializerTests.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InitializerTests.cs new file mode 100644 index 0000000..1fb9ea4 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InitializerTests.cs @@ -0,0 +1,1396 @@ +// 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; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Threading; + +namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.InitializerTests +{ + public static class Extensions + { + public static void Add(this TestCases.CustomList inst, string a, string b) + { + } + + public static void Add(this IList> collection, string key, T value, Func convert = null) + { + } + + public static void Add(this TestCases collection, string key) + { + } + } + + public class TestCases + { + #region Types + public class CustomList : IEnumerable, IEnumerable + { + public IEnumerator GetEnumerator() + { + throw new NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + + public void Add(string name) + { + new Dictionary().Add(name, typeof(T2)); + } + + public void Add(params int[] ints) + { + } + } + + public class C + { + public int Z; + public S Y; + public List L; + + public S this[int index] { + get { +#if CS71 + return default; +#else + return default(S); +#endif + } + set { + } + } + + public S this[object key] { + get { +#if CS71 + return default; +#else + return default(S); +#endif + } + set { + } + } + } + + public struct S + { + public int A; + public int B; + + public int M() + { + return 42; + } + + public S(int a) + { + A = a; + B = 0; + } + } + + private enum MyEnum + { + a, + b + } + + private enum MyEnum2 + { + c, + d + } + + private class Data + { + public List FieldList = new List(); + public MyEnum a { get; set; } + public MyEnum b { get; set; } + public List PropertyList { get; set; } +#if CS60 + public List ReadOnlyPropertyList { get; } +#endif + public Data MoreData { get; set; } + + public StructData NestedStruct { get; set; } + + public Data this[int i] { + get { + return null; + } + set { + } + } + + public Data this[int i, string j] { + get { + return null; + } + set { + } + } + + public event EventHandler TestEvent; + } + + private struct StructData + { + public int Field; + public int Property { get; set; } + + public Data MoreData { get; set; } + + public StructData(int initialValue) + { +#if CS71 + this = default; +#else + this = default(StructData); +#endif + Field = initialValue; + Property = initialValue; + } + } + + public class Item + { + public string Text { get; set; } + + public decimal Value { get; set; } + + public decimal Value2 { get; set; } + + public string Value3 { get; set; } + + public string Value4 { get; set; } + + public string Value5 { get; set; } + + public string Value6 { get; set; } + +#if CS90 + public Fields Value7 { get; set; } +#endif + } + + public class OtherItem + { + public decimal Value { get; set; } + + public decimal Value2 { get; set; } + + public decimal? Nullable { get; set; } + + public decimal? Nullable2 { get; set; } + + public decimal? Nullable3 { get; set; } + + public decimal? Nullable4 { get; set; } + } + + public class OtherItem2 + { + public readonly OtherItem Data; + + public OtherItem Data2 { get; private set; } +#if CS60 + public OtherItem Data3 { get; } +#endif + } + + public class V3f + { + private float x; + private float y; + private float z; + + public V3f(float _x, float _y, float _z) + { + x = _x; + y = _y; + z = _z; + } + } + +#if CS90 + public record Fields + { + public int A; + public double B = 1.0; + public object C; + public dynamic D; + public string S = "abc"; + public Item I; + } + + public record DerivedFields : Fields + { + public ConsoleKey E; + } +#endif + + public interface IData + { + int Property { get; set; } + } + +#if CS90 + public class Issue3392Type + { + public bool Flag { get; init; } + public List List { get; } = new List(); + + public Issue3392Type(object x) + { + + } + } + + private class StructInitPropertiesTest + { + private class TypeA + { + public int A { get; set; } + public int B { get; set; } + } + + private struct TypeB + { + public int A { get; set; } + public int B { get; set; } + } + + private struct TypeC + { + public int A { get; init; } + public int B { get; init; } + } + + private static TypeA TestA() + { + return new TypeA { + A = 1, + B = 2 + }; + } + + private static TypeB TestB() + { + return new TypeB { + A = 1, + B = 2 + }; + } + + private static TypeC TestC() + { + return new TypeC { + A = 1, + B = 2 + }; + } + } +#endif + #endregion + + private S s1; + private S s2; + private static int mixedArrayInitializerValue = 5; + + #region Field initializer tests + private static V3f[] Issue1336_rg0 = new V3f[3] { + new V3f(1f, 1f, 1f), + new V3f(2f, 2f, 2f), + new V3f(3f, 3f, 3f) + }; + + private static V3f[,] Issue1336_rg1 = new V3f[3, 3] { + { + new V3f(1f, 1f, 1f), + new V3f(2f, 2f, 2f), + new V3f(3f, 3f, 3f) + }, + { + new V3f(2f, 2f, 2f), + new V3f(3f, 3f, 3f), + new V3f(4f, 4f, 4f) + }, + { + new V3f(3f, 3f, 3f), + new V3f(4f, 4f, 4f), + new V3f(5f, 5f, 5f) + } + }; + + private static V3f[][] Issue1336_rg1b = new V3f[3][] { + new V3f[3] { + new V3f(1f, 1f, 1f), + new V3f(2f, 2f, 2f), + new V3f(3f, 3f, 3f) + }, + new V3f[3] { + new V3f(2f, 2f, 2f), + new V3f(3f, 3f, 3f), + new V3f(4f, 4f, 4f) + }, + new V3f[3] { + new V3f(3f, 3f, 3f), + new V3f(4f, 4f, 4f), + new V3f(5f, 5f, 5f) + } + }; + + private static V3f[,][] Issue1336_rg1c = new V3f[3, 3][] { + { + new V3f[3] { + new V3f(1f, 1f, 1f), + new V3f(2f, 2f, 2f), + new V3f(3f, 3f, 3f) + }, + new V3f[3] { + new V3f(2f, 2f, 2f), + new V3f(3f, 3f, 3f), + new V3f(4f, 4f, 4f) + }, + new V3f[3] { + new V3f(3f, 3f, 3f), + new V3f(4f, 4f, 4f), + new V3f(5f, 5f, 5f) + } + }, + { + new V3f[3] { + new V3f(1f, 1f, 1f), + new V3f(2f, 2f, 2f), + new V3f(3f, 3f, 3f) + }, + new V3f[3] { + new V3f(2f, 2f, 2f), + new V3f(3f, 3f, 3f), + new V3f(4f, 4f, 4f) + }, + new V3f[3] { + new V3f(3f, 3f, 3f), + new V3f(4f, 4f, 4f), + new V3f(5f, 5f, 5f) + } + }, + { + new V3f[3] { + new V3f(1f, 1f, 1f), + new V3f(2f, 2f, 2f), + new V3f(3f, 3f, 3f) + }, + new V3f[3] { + new V3f(2f, 2f, 2f), + new V3f(3f, 3f, 3f), + new V3f(4f, 4f, 4f) + }, + new V3f[3] { + new V3f(3f, 3f, 3f), + new V3f(4f, 4f, 4f), + new V3f(5f, 5f, 5f) + } + } + }; + + private static V3f[][,] Issue1336_rg1d = new V3f[2][,] { + new V3f[3, 3] { + { + new V3f(1f, 1f, 1f), + new V3f(2f, 2f, 2f), + new V3f(3f, 3f, 3f) + }, + { + new V3f(2f, 2f, 2f), + new V3f(3f, 3f, 3f), + new V3f(4f, 4f, 4f) + }, + { + new V3f(3f, 3f, 3f), + new V3f(4f, 4f, 4f), + new V3f(5f, 5f, 5f) + } + }, + new V3f[3, 3] { + { + new V3f(1f, 1f, 1f), + new V3f(2f, 2f, 2f), + new V3f(3f, 3f, 3f) + }, + { + new V3f(2f, 2f, 2f), + new V3f(3f, 3f, 3f), + new V3f(4f, 4f, 4f) + }, + { + new V3f(3f, 3f, 3f), + new V3f(4f, 4f, 4f), + new V3f(5f, 5f, 5f) + } + } + }; + + private static int[,] Issue1336_rg2 = new int[3, 3] { + { 1, 1, 1 }, + { 1, 1, 1 }, + { 1, 1, 1 } + }; + +#if CS73 && !NET40 + public static ReadOnlySpan StaticData1 => new byte[1] { 0 }; + + public static ReadOnlySpan StaticData3 => new byte[3] { 1, 2, 3 }; + + public static Span StaticData3Span => new byte[3] { 1, 2, 3 }; +#endif +#if CS110 && !NET40 + public static ReadOnlySpan UTF8Literal => "Hello, world!"u8; + public static ReadOnlySpan UTF8LiteralWithNullTerminator => "Hello, world!\0"u8; + public static ReadOnlySpan UTF8LiteralEmpty => ""u8; + public static ReadOnlySpan UTF8LiteralWithEscapeSequences => "line1\nline2\t\"quoted\"\\"u8; + public static ReadOnlySpan UTF8LiteralWithEscapedControlCharacters => "PK\u0003\u0004"u8; +#endif + #endregion + + #region Helper methods used to ensure initializers used within expressions work correctly + private static void X(object a, object b) + { + } + + private static object Y() + { + return null; + } + + public static void TestCall(int a, Thread thread) + { + + } + + public static C TestCall(int a, C c) + { + return c; + } + + private static int GetInt() + { + return 1; + } + + private static string GetString() + { + return "Test"; + } + + private static void NoOp(Guid?[] array) + { + + } + + private void Data_TestEvent(object sender, EventArgs e) + { + throw new NotImplementedException(); + } + #endregion + + #region Array initializers + public static void Array1() + { + X(Y(), new int[10] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + } + + public static void Array2(int a, int b, int c) + { + X(Y(), new int[5] { a, 0, b, 0, c }); + } + + public static int[] DuplicateElementAssignment() + { + // A second store to an element that was already written must stop the array-initializer + // transform from folding these stores into a 'new int[] { ... }'. Doing so would drop the + // earlier store, which is a visible change when its value has side effects. + int[] array = new int[3]; + array[0] = 1; + array[0] = 2; + array[1] = 3; + array[2] = 4; + return array; + } + + public static void NestedArray(int a, int b, int c) + { + X(Y(), new int[3][] { + new int[10] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, + new int[3] { a, b, c }, + new int[6] { 1, 2, 3, 4, 5, 6 } + }); + } + + public static void NestedNullableArray(int a, int b, int c) + { + X(Y(), new int?[3][] { + new int?[11] { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + null + }, + new int?[4] { a, b, c, null }, + new int?[7] { 1, 2, 3, 4, 5, 6, null } + }); + } + + public unsafe static void NestedPointerArray(int a, int b, int c) + { + X(Y(), new void*[3][] { + new void*[1] { null }, + new void*[2] { + (void*)200, + null + }, + new void*[2] { + (void*)100, + null + } + }); + } + + public static void ArrayBoolean() + { + X(Y(), new bool[8] { true, false, true, false, false, false, true, true }); + } + + public static void ArrayByte() + { + X(Y(), new byte[10] { 1, 2, 3, 4, 5, 6, 7, 8, 254, 255 }); + } + + public static void ArraySByte() + { + X(Y(), new sbyte[8] { -128, -127, 0, 1, 2, 3, 4, 127 }); + } + + public static void ArrayShort() + { + X(Y(), new short[5] { -32768, -1, 0, 1, 32767 }); + } + + public static void ArrayUShort() + { + X(Y(), new ushort[6] { 0, 1, 32767, 32768, 65534, 65535 }); + } + + public static void ArrayInt() + { + X(Y(), new int[10] { 1, -2, 2000000000, 4, 5, -6, 7, 8, 9, 10 }); + } + + public static int[] MixedArrayInitializer() + { + return new int[15] { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 2147483647, -2147483648, mixedArrayInitializerValue + }; + } + + public static void ArrayUInt() + { + X(Y(), new uint[10] { 1u, 2000000000u, 3000000000u, 4u, 5u, 6u, 7u, 8u, 9u, 10u }); + } + + public static void ArrayLong() + { + X(Y(), new long[5] { -4999999999999999999L, -1L, 0L, 1L, 4999999999999999999L }); + } + + public static void ArrayULong() + { + X(Y(), new ulong[10] { 1uL, 2000000000uL, 3000000000uL, 4uL, 5uL, 6uL, 7uL, 8uL, 4999999999999999999uL, 9999999999999999999uL }); + } + + public static void ArrayFloat() + { + X(Y(), new float[6] { + -1.5f, + 0f, + 1.5f, + float.NegativeInfinity, + float.PositiveInfinity, + float.NaN + }); + } + + public static void ArrayDouble() + { + X(Y(), new double[6] { + -1.5, + 0.0, + 1.5, + double.NegativeInfinity, + double.PositiveInfinity, + double.NaN + }); + } + + public static void ArrayDecimal() + { + X(Y(), new decimal[6] { -100m, 0m, 100m, -79228162514264337593543950335m, 79228162514264337593543950335m, 0.0000001m }); + } + + public static void ArrayString() + { + X(Y(), new string[4] { "", null, "Hello", "World" }); + } + + public static void ArrayEnum() + { + X(Y(), new MyEnum[4] { + MyEnum.a, + MyEnum.b, + MyEnum.a, + MyEnum.b + }); + } + + public int[,] MultidimensionalInit() + { + return new int[16, 4] { + { 0, 0, 0, 0 }, + { 1, 1, 1, 1 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 1, 0 }, + { 0, 0, 1, 0 }, + { 0, 0, 1, 0 }, + { 0, 0, 1, 0 }, + { 0, 0, 0, 0 }, + { 1, 1, 1, 1 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 1, 0 }, + { 0, 0, 1, 0 }, + { 0, 0, 1, 0 }, + { 0, 0, 1, 0 } + }; + } + + public int[][,] MultidimensionalInit2() + { + return new int[4][,] { + new int[4, 4] { + { 0, 0, 0, 0 }, + { 1, 1, 1, 1 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 } + }, + new int[4, 4] { + { 0, 0, 0, 0 }, + { 1, 1, 1, 1 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 } + }, + new int[4, 4] { + { 0, 0, 1, 0 }, + { 0, 0, 1, 0 }, + { 0, 0, 1, 0 }, + { 0, 0, 1, 0 } + }, + new int[4, 4] { + { 0, 0, 1, 0 }, + { 0, 0, 1, 0 }, + { 0, 0, 1, 0 }, + { 0, 0, 1, 0 } + } + }; + } + + public int[][,,] ArrayOfArrayOfArrayInit() + { + return new int[2][,,] { + new int[2, 3, 3] { + { + { 1, 2, 3 }, + { 4, 5, 6 }, + { 7, 8, 9 } + }, + { + { 11, 12, 13 }, + { 14, 15, 16 }, + { 17, 18, 19 } + } + }, + new int[2, 3, 3] { + { + { 21, 22, 23 }, + { 24, 25, 26 }, + { 27, 28, 29 } + }, + { + { 31, 32, 33 }, + { 34, 35, 36 }, + { 37, 38, 39 } + } + } + }; + } + + public static void RecursiveArrayInitializer() + { + int[] array = new int[3]; + array[0] = 1; + array[1] = 2; + array[2] = array[1] + 1; + array[0] = 0; + } + + public static void InvalidIndices(int a) + { + int[] array = new int[1]; + array[1] = a; + X(Y(), array); + } + + public static void InvalidIndices2(int a) + { +#pragma warning disable 251 + int[] array = new int[1]; + array[-1] = a; + X(Y(), array); +#pragma warning restore + } + + public static void IndicesInWrongOrder(int a, int b) + { + int[] array = new int[5]; + array[2] = b; + array[1] = a; + X(Y(), array); + } + + public int[] IndicesInWrongOrderConstantsFull() + { + int[] array = new int[3]; + array[0] = 0; + array[2] = 1; + array[1] = 2; + return array; + } + + public static byte[] ReverseInitializer(int i) + { + byte[] array = new byte[4]; + array[3] = (byte)i; + array[2] = (byte)(i >> 8); + array[1] = (byte)(i >> 16); + array[0] = (byte)(i >> 24); + return array; + } + + public static void Issue953_MissingNullableSpecifierForArrayInitializer() + { + NoOp(new Guid?[1] { Guid.Empty }); + } + + private void Issue907_Test3(string text) + { + X(Y(), new Dictionary { { "", text } }); + } + + private int[] Issue1383(int i, int[] array) + { + array = new int[4]; + array[i++] = 1; + array[i++] = 2; + return array; + } + + private string[,] Issue1382a() + { + return new string[4, 4] { + { null, "test", "hello", "world" }, + { "test", null, "hello", "world" }, + { "test", "hello", null, "world" }, + { "test", "hello", "world", null } + }; + } + + private string[,] Issue1382b() + { + return new string[4, 4] { + { "test", "hello", "world", null }, + { "test", "hello", null, "world" }, + { "test", null, "hello", "world" }, + { null, "test", "hello", "world" } + }; + } + + private static void OutOfMemory() + { + byte[] array = new byte[int.MaxValue]; + array[0] = 1; + Console.WriteLine(array.Length); + } + +#if !NET40 && CS70 + public static ReadOnlySpan ReadOnlySpanInitializer_ByteArray() + { + return new byte[3] { 1, 2, 3 }; + } + + public static ReadOnlySpan ReadOnlySpanInitializer_Int32Array() + { + return new int[3] { 1, 2, 3 }; + } + + public static string ReadOnlySpanInitializer_OverloadResolution() + { + return SelectSpanOverload((ReadOnlySpan)new byte[3] { 1, 2, 3 }); + } + + private static string SelectSpanOverload(byte[] bytes) + { + return "array"; + } + + private static string SelectSpanOverload(ReadOnlySpan bytes) + { + return "span"; + } +#endif + + #endregion + + #region Object initializers + public C Test1() + { + C c = new C(); + c.L = new List(); + c.L.Add(new S(1)); + return c; + } + + public C Test1Alternative() + { + return TestCall(1, new C { + L = new List { + new S(1) + } + }); + } + + public C Test2() + { + C c = new C(); + c.Z = 1; + c.Z = 2; + return c; + } + + public C Test3() + { + C c = new C(); + c.Y = new S(1); + c.Y.A = 2; + return c; + } + + public C Test3b() + { + return TestCall(0, new C { + Z = 1, + Y = { + A = 2 + } + }); + } + + public C Test4() + { + C c = new C(); + c.Y.A = 1; + c.Z = 2; + c.Y.B = 3; + return c; + } + + public static void ObjectInitializer() + { + X(Y(), new Data { + a = MyEnum.a + }); + } + + public static void NotAnObjectInitializer() + { + Data data = new Data(); + data.a = MyEnum.a; + X(Y(), data); + } + + public static void NotAnObjectInitializerWithEvent() + { + Data data = new Data(); +#if NET50 + data.TestEvent += (object? obj, EventArgs e) => { +#else + data.TestEvent += (object obj, EventArgs e) => { +#endif + Console.WriteLine(); + }; + X(Y(), data); + } + + public static void ObjectInitializerAssignCollectionToField() + { + X(Y(), new Data { + a = MyEnum.a, + FieldList = new List { + MyEnum2.c, + MyEnum2.d + } + }); + } + + public static void ObjectInitializerAddToCollectionInField() + { + X(Y(), new Data { + a = MyEnum.a, + FieldList = { + MyEnum2.c, + MyEnum2.d + } + }); + } + + public static void ObjectInitializerAssignCollectionToProperty() + { + X(Y(), new Data { + a = MyEnum.a, + PropertyList = new List { + MyEnum2.c, + MyEnum2.d + } + }); + } + + public static void ObjectInitializerAddToCollectionInProperty() + { + X(Y(), new Data { + a = MyEnum.a, + PropertyList = { + MyEnum2.c, + MyEnum2.d + } + }); + } + + public static void ObjectInitializerWithInitializationOfNestedObjects() + { + X(Y(), new Data { + MoreData = { + a = MyEnum.a, + MoreData = { + a = MyEnum.b + } + } + }); + } + + public static void ObjectInitializerWithInitializationOfDeeplyNestedObjects() + { + X(Y(), new Data { + a = MyEnum.b, + MoreData = { + a = MyEnum.a, + MoreData = { + MoreData = { + MoreData = { + MoreData = { + MoreData = { + MoreData = { + a = MyEnum.b + } + } + } + } + } + } + } + }); + } + + public static void CollectionInitializerInsideObjectInitializers() + { + X(Y(), new Data { + MoreData = new Data { + a = MyEnum.a, + b = MyEnum.b, + PropertyList = { MyEnum2.c } + } + }); + } + + public static void StructInitializer_DefaultConstructor() + { + X(Y(), new StructData { + Field = 1, + Property = 2 + }); + } + + public void InliningOfStFldTarget() + { + s1 = new S { + A = 24, + B = 42 + }; + s2 = new S { + A = 42, + B = 24 + }; + } + + public static void StructInitializer_ExplicitConstructor() + { + X(Y(), new StructData(0) { + Field = 1, + Property = 2 + }); + } + + public static void StructInitializerWithInitializationOfNestedObjects() + { + X(Y(), new StructData { + MoreData = { + a = MyEnum.a, + FieldList = { + MyEnum2.c, + MyEnum2.d + } + } + }); + } + + public static void StructInitializerWithinObjectInitializer() + { + X(Y(), new Data { + NestedStruct = new StructData(2) { + Field = 1, + Property = 2 + } + }); + } + + public static void Issue270_NestedInitialisers() + { + NumberFormatInfo[] source = null; + + TestCall(0, new Thread(Issue270_NestedInitialisers) { + Priority = ThreadPriority.BelowNormal, + CurrentCulture = new CultureInfo(0) { + DateTimeFormat = new DateTimeFormatInfo { + ShortDatePattern = "ddmmyy" + }, + NumberFormat = source.Where((NumberFormatInfo format) => format.CurrencySymbol == "$").First() + } + }); + } + + public OtherItem2 Issue1345() + { + OtherItem2 otherItem = new OtherItem2(); + otherItem.Data.Nullable = 3m; + return otherItem; + } + + public OtherItem2 Issue1345b() + { + OtherItem2 otherItem = new OtherItem2(); + otherItem.Data2.Nullable = 3m; + return otherItem; + } + +#if CS90 + public Issue3392Type Issue3392(Issue3392Type x) + { + x = new Issue3392Type(null) { + Flag = false + }; + x.List.AddRange(Enumerable.Range(0, 10)); + return x; + } +#endif +#if CS60 + public OtherItem2 Issue1345c() + { + OtherItem2 otherItem = new OtherItem2(); + otherItem.Data3.Nullable = 3m; + return otherItem; + } + + private Data Issue1345_FalsePositive() + { + return new Data { + ReadOnlyPropertyList = { + MyEnum2.c, + MyEnum2.d + } + }; + } +#endif + + private void Issue1250_Test1(MyEnum value) + { + X(Y(), new C { + Z = (int)value + }); + } + + private byte[] Issue1314() + { + return new byte[4] { 0, 1, 2, 255 }; + } + + private void Issue1251_Test(List list, OtherItem otherItem) + { + list.Add(new Item { + Text = "Text", + Value = otherItem.Value, + Value2 = otherItem.Value2, + Value3 = otherItem.Nullable.ToString(), + Value4 = otherItem.Nullable2.ToString(), + Value5 = otherItem.Nullable3.ToString(), + Value6 = otherItem.Nullable4.ToString() + }); + } + + private Data Issue1279(int p) + { + if (p == 1) + { + Data data = new Data(); + data.a = MyEnum.a; + data.TestEvent += Data_TestEvent; + return data; + } + return null; + } + +#if CS90 + private Fields RecordWithNestedClass(Fields input) + { + return input with { + A = 42, + I = new Item { + Value7 = input with { + A = 43 + } + } + }; + } + + public DerivedFields DerivedRecordTest(DerivedFields input) + { + return input with { + A = 42, + D = 43, + I = new Item { + Value7 = input with { + A = 44, + D = 45 + } + } + }; + } +#endif + + private TData GenericObjectInitializer() where TData : IData, new() + { + return new TData { + Property = 42 + }; + } + #endregion + + #region Collection initializer + + public static void ExtensionMethodInCollectionInitializer() + { +#if CS60 + X(Y(), new CustomList { { "1", "2" } }); +#else + CustomList customList = new CustomList(); + customList.Add("1", "2"); + X(Y(), customList); +#endif + } + + public static void NoCollectionInitializerBecauseOfTypeArguments() + { + CustomList customList = new CustomList(); + customList.Add("int"); + Console.WriteLine(customList); + } + + public static TestCases NoCollectionInitializerBecauseOfMissingIEnumerable() + { + TestCases testCases = new TestCases(); + testCases.Add("int"); + testCases.Add("string"); + return testCases; + } + + public static void CollectionInitializerWithParamsMethod() + { + X(Y(), new CustomList { { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 } }); + } + + public static void CollectionInitializerList() + { + X(Y(), new List { 1, 2, 3 }); + } + + public static object RecursiveCollectionInitializer() + { + List list = new List(); + list.Add(list); + return list; + } + + public static void CollectionInitializerDictionary() + { + X(Y(), new Dictionary { + { "First", 1 }, + { "Second", 2 }, + { "Third", 3 } + }); + } + + public static void CollectionInitializerDictionaryWithEnumTypes() + { + X(Y(), new Dictionary { + { + MyEnum.a, + MyEnum2.c + }, + { + MyEnum.b, + MyEnum2.d + } + }); + } + + public static void NotACollectionInitializer() + { + List list = new List(); + list.Add(1); + list.Add(2); + list.Add(3); + X(Y(), list); + } + +#if CS60 + public static void RealDictionaryIndexInitializer() + { + X(Y(), new Dictionary { + ["a"] = 1, + ["b"] = GetInt() + }); + } + + public static void SimpleDictInitializer() + { + X(Y(), new Data { + MoreData = { + a = MyEnum.a, + [2] = null + } + }); + } + + public static void MixedObjectAndDictInitializer() + { + X(Y(), new Data { + MoreData = { + a = MyEnum.a, + [GetInt()] = { + a = MyEnum.b, + FieldList = { MyEnum2.c }, + [GetInt(), GetString()] = new Data(), + [2] = null + } + } + }); + } + + private List> NestedListWithIndexInitializer(MyEnum myEnum) + { + return new List> { + [0] = { 1, 2, 3 }, + [1] = { (int)myEnum } + }; + } + + private void Issue1250_Test2(MyEnum value) + { + X(Y(), new C { [(int)value] = new S((int)value) }); + } + + private void Issue1250_Test3(int value) + { + X(Y(), new C { [value] = new S(value) }); + } + + private void Issue1250_Test4(int value) + { + X(Y(), new C { [(object)value] = new S(value) }); + } + + public static List> Issue1390(IEnumerable tokens, bool alwaysAllowAdministrators, char wireDelimiter) + { + return new List> { + { + "tokens", + string.Join(wireDelimiter.ToString(), tokens), + (Func)null + }, + { + "alwaysAllowAdministrators", + alwaysAllowAdministrators.ToString(), + (Func)null + }, + { + "delimiter", + wireDelimiter.ToString(), + (Func)null + } + }; + } + +#endif + #endregion + } +} diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Records.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Records.cs new file mode 100644 index 0000000..1e078e5 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Records.cs @@ -0,0 +1,532 @@ +using System; +#if CS100 +using System.Runtime.InteropServices; +using System.Text; +#endif + +namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty +{ + internal class RecordClasses + { + public record Base(string A); + + public record CopyCtor(string A) + { + protected CopyCtor(CopyCtor _) + { + } + } + + public record Derived(int B) : Base(B.ToString()); + + public record BaseRecordWithObject(object Id); + + public record DerivedRecordWithString : BaseRecordWithObject + { + public DerivedRecordWithString(string Id) + : base(Id) + { + } + } + + public record Empty; + + public record EmptyWithStaticField + { + public static readonly Empty X = new Empty(); + } + + public record Fields + { + public int A; + public double B = 1.0; + public object C; + public dynamic D; + public string S = "abc"; + } + + public record Interface(int B) : IRecord; + + public interface IRecord + { + } + + public record Pair + { + public A First { get; init; } + public B Second { get; init; } + } + + public record PairWithPrimaryCtor(A First, B Second); + + public record PrimaryCtor(int A, string B); + public record PrimaryCtorWithAttribute([RecordTest("param")][property: RecordTest("property")][field: RecordTest("field")] int a); + public record PrimaryCtorWithField(int A, string B) + { + public double C = 1.0; + public string D = A + B; + } + public record PrimaryCtorWithInParameter(in int A, in string B); + public record PrimaryCtorWithProperty(int A, string B) + { + public double C { get; init; } = 1.0; + public string D { get; } = A + B; + } + + public record Properties + { + public int A { get; set; } + public int B { get; } + public int C => 43; + public object O { get; set; } + public string S { get; set; } + public dynamic D { get; set; } + + public Properties() + { + B = 42; + } + } + +#if CS110 + public record WithRequiredMembers + { + public required Type ServiceType { get; init; } + + public required object ServiceKey { get; init; } + } +#endif + + [AttributeUsage(AttributeTargets.All)] + public class RecordTestAttribute : Attribute + { + public RecordTestAttribute(string name) + { + } + } + + public sealed record Sealed(string A); + + public sealed record SealedDerived(int B) : Base(B.ToString()); + + public class WithExpressionTests + { + public Fields Test(Fields input) + { + return input with { + A = 42, + B = 3.141, + C = input + }; + } + public Fields Test2(Fields input) + { + return input with { + A = 42, + B = 3.141, + C = input with { + A = 43 + } + }; + } + } + + public abstract record WithNestedRecords + { + public record A : WithNestedRecords + { + public override string AbstractProp => "A"; + } + + public record B : WithNestedRecords + { + public override string AbstractProp => "B"; + + public int? Value { get; set; } + } + + public record DerivedGeneric : Pair where T : struct + { + public bool Flag; + } + + public abstract string AbstractProp { get; } + } + + public abstract record BaseRecord + { + public string Name { get; } + public object Value { get; } + public bool Encode { get; } + + protected BaseRecord(string name, object value, bool encode) + { + Name = name; + Value = value; + Encode = encode; + } + } + + public record DerivedRecord(string name, object value, bool encode = true) : BaseRecord(string.IsNullOrEmpty(name) ? "name" : name, value, encode); + + public record DefaultValuesRecord() : DerivedRecord("default", 42, encode: false); + + public record RecordWithProtectedMember(int Value) + { + protected int Double => Value * 2; + } + + public record InheritedRecordWithAdditionalMember(int Value) : RecordWithProtectedMember(Value) + { + public int MoreData { get; set; } + } + + public record InheritedRecordWithAdditionalParameter(int Value, int Value2) : RecordWithProtectedMember(Value); + + public record BaseWithString(string S); + + public record DerivedWithAdditionalInt(int I) : BaseWithString(I.ToString()); + + public record DerivedWithNoAdditionalProperty(string S) : BaseWithString(S); + + public record DerivedWithAdditionalProperty(string S2) : BaseWithString(S2); + + public record DerivedWithAdditionalPropertyDifferentAccessor(string S) : BaseWithString(S) + { + public string S2 { get; set; } = S; + } + + public record MultipleCtorsChainedNoPrimaryCtor + { + public int A { get; init; } + public string B { get; init; } + + public double C { get; init; } + + public MultipleCtorsChainedNoPrimaryCtor(double c) + { + A = 0; + B = null; + C = c; + } + + public MultipleCtorsChainedNoPrimaryCtor(int a) + : this(3.14) + { + A = a; + } + + public MultipleCtorsChainedNoPrimaryCtor(string b) + : this(4.13) + { + B = b; + } + } + + public record UnexpectedCodeInCtor + { + public int A { get; init; } + public string B { get; init; } + + public UnexpectedCodeInCtor(int A, string B) + { + this.A = A; + this.B = B; + Console.WriteLine(); + } + + public UnexpectedCodeInCtor(int A) + : this(A, null) + { + this.A = A; + } + } + + private record RecordWithMultipleInitializerAssignmentsInPrimaryCtor(string name, string? value, in object encode) + { + public string? Value { get; } = name; + + public string Name { get; } = value; + + private string? WebValue { get; } = (name != null) ? "111" : value; + + private string? WebValue2; + } + + public record FieldBeforeProperty(int ID, string Text) + { + public int Field; + + public int Property { get; set; } + } + + public record FieldsAndPropertiesInterleaved(int ID) + { + public int First; + + public int Middle { get; set; } + + public int Last; + } + + public record PrivateFieldAndComputedProperty(int ID) + { + public int PublicField; + + private int privateField; + + public int Computed => privateField + PublicField; + + public int Auto { get; set; } + } + + public record DerivedWithInterleavedMembers(int B) : Base(B.ToString()) + { + public int Field; + + public int Property { get; set; } + } + } + +#if CS100 + internal class RecordStructs + { + public readonly record struct ReadonlyPoint(int X, int Y); + + public record struct Base(string A); + + public record CopyCtor(string A) + { + protected CopyCtor(CopyCtor _) + { + } + } + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public record struct Empty; + + public record struct Fields + { + public int A; + public double B; + public object C; + public dynamic D; + public string S; + } + + public record struct Interface(int B) : IRecord; + + public interface IRecord + { + } + + public record struct Pair + { + public A First { get; init; } + public B Second { get; init; } + } + + public record struct PairWithPrimaryCtor(A First, B Second); + + public readonly record struct BoundsInfo(int Profile, Bounds Bounds); + + public readonly record struct Bounds(ulong Small, ulong Large); + + public record struct PrimaryCtor(int A, string B); + + public record struct MultipleCtorsNoPrimaryCtor + { + public Guid Id { get; } + + public MultipleCtorsNoPrimaryCtor() + { + Id = Guid.NewGuid(); + } + + public MultipleCtorsNoPrimaryCtor(Guid id) + { + Id = id; + } + } + + public record struct MultipleCtorsChainedNoPrimaryCtor + { + public int A { get; init; } + public string B { get; init; } + + public double C { get; init; } + + public MultipleCtorsChainedNoPrimaryCtor(double c) + { + A = 0; + B = null; + C = c; + } + + public MultipleCtorsChainedNoPrimaryCtor(int a) + : this(3.14) + { + A = a; + } + + public MultipleCtorsChainedNoPrimaryCtor(string b) + : this(4.13) + { + B = b; + } + } + + public record struct PrimaryCtorWithAttribute([RecordTest("param")][property: RecordTest("property")][field: RecordTest("field")] int a); + public record struct PrimaryCtorWithField(int A, string B) + { + public double C = 1.0; + public string D = A + B; + } + public record struct PrimaryCtorWithInParameter(in int A, in string B); + public record struct PrimaryCtorWithProperty(int A, string B) + { + public double C { get; init; } = 1.0; + public string D { get; } = A + B; + } + + public record struct Properties + { + public int A { get; set; } + public int B { get; } + public int C => 43; + public object O { get; set; } + public string S { get; set; } + public dynamic D { get; set; } + + public Properties() + { + A = 41; + B = 42; + O = null; + S = "Hello"; + D = null; + } + } + + public record struct PropertiesWithInitializers() + { + public int A { get; set; } = 41; + public int B { get; } = 42; + public int C => 43; + public object O { get; set; } = null; + public string S { get; set; } = "Hello"; + public dynamic D { get; set; } = null; + } + + [AttributeUsage(AttributeTargets.All)] + public class RecordTestAttribute : Attribute + { + public RecordTestAttribute(string name) + { + } + } + + public class WithExpressionTests + { + public Fields Test(Fields input) + { + return input with { + A = 42, + B = 3.141, + C = input + }; + } + public Fields Test2(Fields input) + { + return input with { + A = 42, + B = 3.141, + C = input with { + A = 43 + } + }; + } + } +#if CS110 + public record struct WithRequiredMembers + { + public int A { get; set; } + public required double B { get; set; } + public object C; + public required dynamic D; + } +#endif + public record struct RecordWithMultipleCtors + { + public int A { get; set; } + + public RecordWithMultipleCtors() + { + A = 42; + } + + public RecordWithMultipleCtors(int A) + { + this.A = A; + } + + public RecordWithMultipleCtors(string A) + { + this.A = int.Parse(A); + } + } + + public record struct RecordCtorChain(int A, string B) + { +#if EXPECTED_OUTPUT + public double C = 0.0; +#else + public double C; +#endif + public RecordCtorChain(int A) + : this(A, "default") + { + C = 3.14; + } + public RecordCtorChain(string B) + : this(42, B) + { + C = 1.41; + } + } + + private static BoundsInfo GetInfo() + { + return new BoundsInfo(1, new Bounds(2uL, 3uL)); + } + + public static void NestedDeconstruction() + { + var (value, (value2, value3)) = GetInfo(); + Console.WriteLine(value); + Console.WriteLine(value2); + Console.WriteLine(value3); + } + } + + internal class RecordsWithCustomSynthesizedMembers + { + public record WithCustomToString(int A) + { + public sealed override string ToString() + { + return "custom"; + } + } + + public record WithCustomPrintMembers(int A) + { + protected virtual bool PrintMembers(StringBuilder builder) + { + builder.Append("X"); + return true; + } + } + } +#endif +} diff --git a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs index 2833ebc..6c0c98c 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs @@ -942,10 +942,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms for (int i = 0; i < childIndex; ++i) { ILInstruction predecessor = inst.Parent.Children[i]; - // The variable of a named argument only exists within its block, - // so only the stored value can interfere with the moved expression. - if (predecessor is StLoc { Variable.Kind: VariableKind.NamedArgument } namedArgument) - predecessor = namedArgument.Value; if (!IsSafeForInlineOver(predecessor, expressionBeingMoved)) return false; }