diff --git a/ICSharpCode.Decompiler.Tests/DecompilerSettingsTests.cs b/ICSharpCode.Decompiler.Tests/DecompilerSettingsTests.cs
index 2645e6f936..450d84e014 100644
--- a/ICSharpCode.Decompiler.Tests/DecompilerSettingsTests.cs
+++ b/ICSharpCode.Decompiler.Tests/DecompilerSettingsTests.cs
@@ -49,5 +49,12 @@ public void GetMinimumRequiredVersionReturnsTheHighestEnabledFeatureVersion()
settings.ParamsCollections = false;
Assert.That(settings.GetMinimumRequiredVersion(), Is.EqualTo(LanguageVersion.CSharp11_0));
}
+
+ [Test]
+ public void CollectionExpressionsRequireCSharp12()
+ {
+ Assert.That(new DecompilerSettings(LanguageVersion.CSharp11_0).CollectionExpressions, Is.False);
+ Assert.That(new DecompilerSettings(LanguageVersion.CSharp12_0).CollectionExpressions, Is.True);
+ }
}
}
diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
index 714454f75b..3f4399b760 100644
--- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
+++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
@@ -230,6 +230,8 @@
+
+
diff --git a/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs
index 7e9f8df947..d3c50f2797 100644
--- a/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs
+++ b/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs
@@ -429,14 +429,33 @@ public async Task Issue3729()
await Run();
}
+ [Test]
+ public async Task LateBaseConstructorCall()
+ {
+ await Run();
+ }
+
+ [Test]
+ public async Task CompilerGeneratedAutoProperty()
+ {
+ await Run();
+ }
+
+ [Test]
+ public async Task MissingBaseConstructor()
+ {
+ await Run(expectedText: "//IL_0001: Unknown result type (might be due to invalid IL or missing references)");
+ }
+
async Task Run([CallerMemberName] string testName = null, DecompilerSettings settings = null,
- AssemblerOptions assemblerOptions = AssemblerOptions.Library)
+ AssemblerOptions assemblerOptions = AssemblerOptions.Library, string expectedText = null)
{
if (settings == null)
{
// never use file-scoped namespaces, unless explicitly specified
settings = new DecompilerSettings { FileScopedNamespaces = false };
}
+ settings.CollectionExpressions = false;
var ilFile = Path.Combine(TestCasePath, testName + ".il");
var csFile = Path.Combine(TestCasePath, testName + ".cs");
@@ -444,6 +463,8 @@ async Task Run([CallerMemberName] string testName = null, DecompilerSettings set
var decompiled = await Tester.DecompileCSharp(executable, settings).ConfigureAwait(false);
CodeAssert.FilesAreEqual(csFile, decompiled, ["EXPECTED_OUTPUT"]);
+ if (expectedText != null)
+ Assert.That(File.ReadAllText(decompiled), Does.Contain(expectedText));
Tester.RepeatOnIOError(() => File.Delete(decompiled));
}
diff --git a/ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs b/ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs
index cdffbe5020..5be0353008 100644
--- a/ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs
+++ b/ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs
@@ -23,6 +23,8 @@
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using System.Runtime.CompilerServices;
+using System.Security.Cryptography;
+using System.Text;
using System.Threading.Tasks;
using ICSharpCode.Decompiler.CSharp;
@@ -58,6 +60,29 @@ public void HelloWorld()
TestSequencePoints();
}
+ [Test]
+ public void GlobalNamespaceDocument()
+ {
+ (string peFileName, _) = CompileTestCase(nameof(GlobalNamespaceDocument));
+ var module = new PEFile(peFileName);
+ var resolver = new UniversalAssemblyResolver(peFileName, false,
+ module.Metadata.DetectTargetFrameworkId(), null, PEStreamOptions.PrefetchEntireImage);
+ var decompiler = new CSharpDecompiler(module, resolver, new DecompilerSettings());
+ const string sourceText = "source text";
+ using var generatedPdb = new MemoryStream();
+ new PortablePdbWriter {
+ NoLogo = true,
+ SourceTextProvider = _ => sourceText,
+ }.WritePdb(module, decompiler, new DecompilerSettings(), generatedPdb);
+
+ generatedPdb.Position = 0;
+ var reader = MetadataReaderProvider.FromPortablePdbStream(generatedPdb).GetMetadataReader();
+ var document = reader.GetDocument(reader.Documents.Single());
+ Assert.That(reader.GetString(document.Name), Is.EqualTo("GlobalNamespaceDocument.cs"));
+ Assert.That(reader.GetBlobBytes(document.Hash),
+ Is.EqualTo(SHA256.HashData(Encoding.UTF8.GetBytes(sourceText))));
+ }
+
[Test]
public void ForLoopTests()
{
diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
index 01b6228c96..cf1b9ae798 100644
--- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
+++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
@@ -466,7 +466,8 @@ public async Task UnsafeCode([ValueSource(nameof(defaultOptions))] CompilerOptio
[Test]
public async Task ConstructorInitializers([ValueSource(nameof(defaultOptionsWithMcs))] CompilerOptions cscOptions)
{
- await RunForLibrary(cscOptions: cscOptions | CompilerOptions.ProcessXmlDoc);
+ await RunForLibrary(cscOptions: cscOptions | CompilerOptions.ProcessXmlDoc,
+ configureDecompiler: settings => settings.CollectionExpressions = Tester.GetPreprocessorSymbols(cscOptions).Contains("CS120"));
}
[Test]
@@ -744,7 +745,8 @@ public async Task RefLocalsAndReturns([ValueSource(nameof(roslyn2OrNewerOptions)
[Test]
public async Task CachedReadOnlySpanInitialization([ValueSource(nameof(roslyn2OrNewerOptions))] CompilerOptions cscOptions)
{
- await RunForLibrary(cscOptions: cscOptions);
+ await RunForLibrary(cscOptions: cscOptions,
+ configureDecompiler: settings => settings.CollectionExpressions = Tester.GetPreprocessorSymbols(cscOptions).Contains("CS120"));
}
[Test]
@@ -1040,6 +1042,13 @@ public async Task InlineArrayTests([ValueSource(nameof(roslyn4OrNewerOptions))]
await RunForLibrary(cscOptions: cscOptions);
}
+ [Test]
+ public async Task CollectionExpressions([ValueSource(nameof(roslyn4OrNewerOptions))] CompilerOptions cscOptions)
+ {
+ await RunForLibrary(cscOptions: cscOptions,
+ configureDecompiler: settings => settings.CollectionExpressions = Tester.GetPreprocessorSymbols(cscOptions).Contains("CS120"));
+ }
+
[Test]
public async Task Issue3684([ValueSource(nameof(roslyn4OrNewerOptions))] CompilerOptions cscOptions)
{
@@ -1080,6 +1089,7 @@ async Task Run([CallerMemberName] string testName = null, AssemblerOptions asmOp
// 2. Decompile
var settings = Tester.GetSettings(cscOptions);
+ settings.CollectionExpressions = false;
configureDecompiler?.Invoke(settings);
var decompiled = await Tester.DecompileCSharp(exeFile, settings).ConfigureAwait(false);
diff --git a/ICSharpCode.Decompiler.Tests/ProjectDecompiler/TargetFrameworkTests.cs b/ICSharpCode.Decompiler.Tests/ProjectDecompiler/TargetFrameworkTests.cs
index 6391ca96ba..db82e48f83 100644
--- a/ICSharpCode.Decompiler.Tests/ProjectDecompiler/TargetFrameworkTests.cs
+++ b/ICSharpCode.Decompiler.Tests/ProjectDecompiler/TargetFrameworkTests.cs
@@ -17,6 +17,8 @@
// DEALINGS IN THE SOFTWARE.
using System;
+using System.IO;
+using System.Reflection;
using ICSharpCode.Decompiler.CSharp.ProjectDecompiler;
using ICSharpCode.Decompiler.Metadata;
@@ -129,5 +131,63 @@ public void VerifyUniversalAssemblyResolverParseTargetFramework(string targetFra
Assert.That(id, Is.EqualTo(identifier));
Assert.That(v.ToString(3), Is.EqualTo(version));
}
+
+ [TestCase(TargetFrameworkIdentifier.NET, true)]
+ [TestCase(TargetFrameworkIdentifier.NETCoreApp, true)]
+ [TestCase(TargetFrameworkIdentifier.NETStandard, true)]
+ [TestCase(TargetFrameworkIdentifier.Silverlight, false)]
+ public void VerifyUseOfDotNetCorePathFinder(TargetFrameworkIdentifier identifier, bool expected)
+ {
+ Assert.That(UniversalAssemblyResolver.UsesDotNetCorePathFinder(identifier), Is.EqualTo(expected));
+ }
+ [Test]
+ public void NetStandardResolvesSharedRuntimeAssembly()
+ {
+ var reference = AssemblyNameReference.Parse(
+ "System.Memory, Version=4.0.1.2, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51");
+ var resolver = new UniversalAssemblyResolver(null, false, ".NETStandard,Version=v2.0");
+
+ var file = resolver.FindAssemblyFile(reference);
+
+ Assert.That(file, Is.Not.Null);
+ Assert.That(File.Exists(file), Is.True);
+ }
+
+ [Test]
+ public void NetCoreAppResolvesCompatibleSharedRuntimeAssembly()
+ {
+ var reference = AssemblyNameReference.Parse(
+ "System.Text.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51");
+ var resolver = new UniversalAssemblyResolver(null, false, ".NETCoreApp,Version=v3.1");
+
+ var file = resolver.FindAssemblyFile(reference);
+
+ Assert.That(file, Is.Not.Null);
+ Assert.That(File.Exists(file), Is.True);
+ Assert.That(AssemblyName.GetAssemblyName(file!).Version, Is.GreaterThanOrEqualTo(reference.Version));
+ }
+
+ [Test]
+ public void LaterRuntimeAssemblyResolvesWithoutBecomingImplicitProjectReference()
+ {
+ var reference = AssemblyNameReference.Parse(
+ "System.Net.ServerSentEvents, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
+ var resolver = new UniversalAssemblyResolver(null, false, ".NETCoreApp,Version=v8.0");
+
+ Assert.That(resolver.FindAssemblyFile(reference), Is.Not.Null);
+ Assert.That(resolver.IsSharedAssembly(reference, out _), Is.False);
+ }
+
+ [TestCase(TargetFrameworkIdentifier.NETStandard, ".NETStandard,Version=v2.0", PlatformID.Win32NT, false)]
+ [TestCase(TargetFrameworkIdentifier.NET, ".NETFramework,Version=v4.7.2", PlatformID.Win32NT, false)]
+ [TestCase(TargetFrameworkIdentifier.NETCoreApp, ".NETCoreApp,Version=v3.1", PlatformID.Win32NT, true)]
+ [TestCase(TargetFrameworkIdentifier.NET, ".NETCoreApp,Version=v10.0", PlatformID.Win32NT, true)]
+ [TestCase(TargetFrameworkIdentifier.NETStandard, ".NETStandard,Version=v2.0", PlatformID.Unix, true)]
+ public void VerifyHostRuntimeFallback(TargetFrameworkIdentifier identifier, string targetFramework,
+ PlatformID platform, bool expected)
+ {
+ Assert.That(UniversalAssemblyResolver.ShouldUseHostRuntimeFallback(identifier, targetFramework, platform),
+ Is.EqualTo(expected));
+ }
}
}
diff --git a/ICSharpCode.Decompiler.Tests/ProjectDecompiler/WholeProjectDecompilerTests.cs b/ICSharpCode.Decompiler.Tests/ProjectDecompiler/WholeProjectDecompilerTests.cs
index 4d0d25c8e7..8ae06565e7 100644
--- a/ICSharpCode.Decompiler.Tests/ProjectDecompiler/WholeProjectDecompilerTests.cs
+++ b/ICSharpCode.Decompiler.Tests/ProjectDecompiler/WholeProjectDecompilerTests.cs
@@ -28,6 +28,9 @@
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+
using NUnit.Framework;
namespace ICSharpCode.Decompiler.Tests.ProjectDecompiler;
@@ -134,6 +137,75 @@ public void OneFailingResourceDoesNotDropTheOthers()
}
}
+ [Test]
+ public void HiddenReferencedTypesDoNotCreateEmptyProjectFiles()
+ {
+ string assemblyPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + ".dll");
+ try
+ {
+ CompileCollectionExpressionAssembly(assemblyPath);
+ TestFriendlyProjectDecompiler decompiler = new(new UniversalAssemblyResolver(assemblyPath, false, null));
+ decompiler.Settings.CollectionExpressions = true;
+
+ using PEFile module = new(assemblyPath);
+ decompiler.DecompileProject(module, Path.GetTempPath(), new StringWriter());
+
+ using (Assert.EnterMultipleScope())
+ {
+ Assert.That(decompiler.Files.Keys.Select(Path.GetFileName),
+ Has.None.StartsWith("--z__ReadOnly"));
+ string source = decompiler.Files.Single(file => Path.GetFileName(file.Key) == "CollectionSource.cs")
+ .Value.ToString();
+ Assert.That(source, Does.Contain("Consume([1]);"));
+ Assert.That(source, Does.Contain("Consume([1, 2, 3]);"));
+ Assert.That(source, Does.Match(@"Consume\(\[\.\. \w+\]\);"));
+ }
+ }
+ finally
+ {
+ File.Delete(assemblyPath);
+ }
+ }
+
+ static void CompileCollectionExpressionAssembly(string assemblyPath)
+ {
+ const string source = """
+ using System.Collections.Generic;
+
+ public static class CollectionSource
+ {
+ public static void Call(int[] values)
+ {
+ Consume([1]);
+ Consume([1, 2, 3]);
+ Consume([0, .. values, 4]);
+ }
+
+ private static void Consume(IReadOnlyList values)
+ {
+ }
+ }
+ """;
+ string runtimeDirectory = Path.GetDirectoryName(typeof(object).Assembly.Location)!;
+ var compilation = CSharpCompilation.Create(
+ "CollectionExpressionProject",
+ new[] {
+ CSharpSyntaxTree.ParseText(source,
+ new CSharpParseOptions(Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp12))
+ },
+ new[] {
+ MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
+ MetadataReference.CreateFromFile(Path.Combine(runtimeDirectory, "System.Runtime.dll"))
+ },
+ new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary,
+ optimizationLevel: OptimizationLevel.Release));
+
+ using FileStream output = File.Create(assemblyPath);
+ var result = compilation.Emit(output);
+ Assert.That(result.Success, Is.True,
+ string.Join(Environment.NewLine, result.Diagnostics.Select(diagnostic => diagnostic.ToString())));
+ }
+
sealed class ThrowingAstTransform(string typeName) : IAstTransform
{
public const string Failure = "Simulated AST transform failure";
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/CachedReadOnlySpanFromLazyCache.cs b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/CachedReadOnlySpanFromLazyCache.cs
index d29d6c3791..eb7d928952 100644
--- a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/CachedReadOnlySpanFromLazyCache.cs
+++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/CachedReadOnlySpanFromLazyCache.cs
@@ -4,10 +4,6 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty
{
public static class CachedReadOnlySpanFromLazyCache
{
- public static ReadOnlySpan NewLine {
- get {
- return new ReadOnlySpan(new char[2] { '\r', '\n' });
- }
- }
+ public static ReadOnlySpan NewLine => new ReadOnlySpan(new char[2] { '\r', '\n' });
}
}
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/CompilerGeneratedAutoProperty.cs b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/CompilerGeneratedAutoProperty.cs
new file mode 100644
index 0000000000..e1dc5d088b
--- /dev/null
+++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/CompilerGeneratedAutoProperty.cs
@@ -0,0 +1,26 @@
+using System.Runtime.CompilerServices;
+
+namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty
+{
+#if !EXPECTED_OUTPUT
+ public struct MissingMemory
+ {
+ }
+#endif
+
+ [CompilerGenerated]
+ public sealed class CompilerGeneratedAutoProperty
+ {
+ public string Name { get; }
+
+ public CompilerGeneratedAutoProperty(string name)
+ {
+ Name = name;
+ }
+ }
+
+ public class UnresolvedGenericAutoProperty
+ {
+ public MissingMemory Data { get; set; }
+ }
+}
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/CompilerGeneratedAutoProperty.il b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/CompilerGeneratedAutoProperty.il
new file mode 100644
index 0000000000..276f52dc41
--- /dev/null
+++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/CompilerGeneratedAutoProperty.il
@@ -0,0 +1,79 @@
+.assembly extern System.Runtime
+{
+ .publickeytoken = (B7 7A 5C 56 19 34 E0 89)
+ .ver 4:0:0:0
+}
+
+.assembly CompilerGeneratedAutoProperty
+{
+ .hash algorithm 0x00008004
+ .ver 0:0:0:0
+}
+
+.assembly extern MissingGenericAssembly
+{
+ .ver 1:0:0:0
+}
+
+.class public auto ansi sealed beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.ILPretty.CompilerGeneratedAutoProperty
+ extends [System.Runtime]System.Object
+{
+ .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (01 00 00 00)
+ .field private initonly string 'k__BackingField'
+
+ .method public hidebysig specialname instance string get_Name() cil managed
+ {
+ .maxstack 8
+ IL_0000: ldarg.0
+ IL_0001: ldfld string ICSharpCode.Decompiler.Tests.TestCases.ILPretty.CompilerGeneratedAutoProperty::'k__BackingField'
+ IL_0006: ret
+ }
+
+ .method public hidebysig specialname rtspecialname instance void .ctor(string name) cil managed
+ {
+ .maxstack 8
+ IL_0000: ldarg.0
+ IL_0001: call instance void [System.Runtime]System.Object::.ctor()
+ IL_0006: ldarg.0
+ IL_0007: ldarg.1
+ IL_0008: stfld string ICSharpCode.Decompiler.Tests.TestCases.ILPretty.CompilerGeneratedAutoProperty::'k__BackingField'
+ IL_000d: ret
+ }
+
+ .property instance string Name()
+ {
+ .get instance string ICSharpCode.Decompiler.Tests.TestCases.ILPretty.CompilerGeneratedAutoProperty::get_Name()
+ }
+}
+
+.class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.ILPretty.UnresolvedGenericAutoProperty`1
+ extends [System.Runtime]System.Object
+{
+ .field private valuetype [MissingGenericAssembly]MissingMemory`1 'k__BackingField'
+ .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (01 00 00 00)
+
+ .method public hidebysig specialname instance valuetype [MissingGenericAssembly]MissingMemory`1 get_Data() cil managed
+ {
+ .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (01 00 00 00)
+ .maxstack 8
+ IL_0000: ldarg.0
+ IL_0001: ldfld valuetype [MissingGenericAssembly]MissingMemory`1 ICSharpCode.Decompiler.Tests.TestCases.ILPretty.UnresolvedGenericAutoProperty`1::'k__BackingField'
+ IL_0006: ret
+ }
+
+ .method public hidebysig specialname instance void set_Data(valuetype [MissingGenericAssembly]MissingMemory`1 'value') cil managed
+ {
+ .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (01 00 00 00)
+ .maxstack 8
+ IL_0000: ldarg.0
+ IL_0001: ldarg.1
+ IL_0002: stfld valuetype [MissingGenericAssembly]MissingMemory`1 ICSharpCode.Decompiler.Tests.TestCases.ILPretty.UnresolvedGenericAutoProperty`1::'k__BackingField'
+ IL_0007: ret
+ }
+
+ .property instance valuetype [MissingGenericAssembly]MissingMemory`1 Data()
+ {
+ .get instance valuetype [MissingGenericAssembly]MissingMemory`1 ICSharpCode.Decompiler.Tests.TestCases.ILPretty.UnresolvedGenericAutoProperty`1::get_Data()
+ .set instance void ICSharpCode.Decompiler.Tests.TestCases.ILPretty.UnresolvedGenericAutoProperty`1::set_Data(valuetype [MissingGenericAssembly]MissingMemory`1)
+ }
+}
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue3729.cs b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue3729.cs
index 4ac1684c30..8b073936a8 100644
--- a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue3729.cs
+++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue3729.cs
@@ -66,6 +66,13 @@ public void TestRefTypeNewobj()
MyClass value = new MyClass();
Console.WriteLine(value);
}
+
+ public void TestUnresolvedStructMemberCalls()
+ {
+ MyEnumerator val = default;
+ val.MoveNext();
+ ((IDisposable)val/*cast due to constrained. prefix*/).Dispose();
+ }
}
public class Issue3729_DerivedFromUnknown : MissingBase
{
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue3729.il b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue3729.il
index f468655e6e..8ed0374995 100644
--- a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue3729.il
+++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue3729.il
@@ -177,6 +177,23 @@
IL_000c: ret
}
+ .method public hidebysig
+ instance void TestUnresolvedStructMemberCalls () cil managed
+ {
+ .maxstack 1
+ .locals init (
+ [0] valuetype [Library1]Library1.MyEnumerator
+ )
+
+ IL_0000: ldloca.s 0
+ IL_0002: call instance bool [Library1]Library1.MyEnumerator::MoveNext()
+ IL_0007: pop
+ IL_0008: ldloca.s 0
+ IL_000a: constrained. [Library1]Library1.MyEnumerator
+ IL_0010: callvirt instance void [System.Runtime]System.IDisposable::Dispose()
+ IL_0015: ret
+ }
+
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/LateBaseConstructorCall.cs b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/LateBaseConstructorCall.cs
new file mode 100644
index 0000000000..aa7abd40d0
--- /dev/null
+++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/LateBaseConstructorCall.cs
@@ -0,0 +1,44 @@
+namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty
+{
+ public class LateBaseConstructorCall
+ {
+ private static void Initialize()
+ {
+ }
+
+ public LateBaseConstructorCall()
+ {
+ Initialize();
+ }
+ }
+
+ public class SpilledArgumentSource
+ {
+ public string Content;
+ public string Refusal;
+ public object Output;
+ public string Function;
+ }
+
+ public class SpilledConstructorInitializer
+ {
+ private SpilledConstructorInitializer(SpilledRole role, string content, in SpilledPatch patch, string refusal, string participantName, object output, object tools, string function)
+ {
+ }
+
+ public SpilledConstructorInitializer(SpilledArgumentSource source)
+ : this(content: source?.Content, patch: default, refusal: source?.Refusal, participantName: null, function: source?.Function, role: SpilledRole.Assistant, output: (source?.Output != null) ? new object() : null, tools: null)
+ {
+ }
+ }
+
+ public struct SpilledPatch
+ {
+ public int Value;
+ }
+
+ public enum SpilledRole
+ {
+ Assistant = 2
+ }
+}
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/LateBaseConstructorCall.il b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/LateBaseConstructorCall.il
new file mode 100644
index 0000000000..2c026cbbb9
--- /dev/null
+++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/LateBaseConstructorCall.il
@@ -0,0 +1,135 @@
+.assembly extern System.Runtime
+{
+ .publickeytoken = (B7 7A 5C 56 19 34 E0 89)
+ .ver 4:0:0:0
+}
+
+.assembly LateBaseConstructorCall
+{
+ .hash algorithm 0x00008004
+ .ver 0:0:0:0
+}
+
+.class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.ILPretty.LateBaseConstructorCall
+ extends [System.Runtime]System.Object
+{
+ .method private hidebysig static void Initialize() cil managed
+ {
+ .maxstack 8
+ IL_0000: ret
+ }
+
+ .method public hidebysig specialname rtspecialname instance void .ctor() cil managed
+ {
+ .maxstack 8
+ IL_0000: call void ICSharpCode.Decompiler.Tests.TestCases.ILPretty.LateBaseConstructorCall::Initialize()
+ IL_0005: ldarg.0
+ IL_0006: call instance void [System.Runtime]System.Object::.ctor()
+ IL_000b: ret
+ }
+}
+
+.class public auto ansi sealed ICSharpCode.Decompiler.Tests.TestCases.ILPretty.SpilledRole
+ extends [System.Runtime]System.Enum
+{
+ .field public specialname rtspecialname int32 value__
+ .field public static literal valuetype ICSharpCode.Decompiler.Tests.TestCases.ILPretty.SpilledRole Assistant = int32(2)
+}
+
+.class public sequential ansi sealed beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.ILPretty.SpilledPatch
+ extends [System.Runtime]System.ValueType
+{
+ .field public int32 Value
+}
+
+.class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.ILPretty.SpilledArgumentSource
+ extends [System.Runtime]System.Object
+{
+ .field public string Content
+ .field public string Refusal
+ .field public object Output
+ .field public string Function
+}
+
+.class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.ILPretty.SpilledConstructorInitializer
+ extends [System.Runtime]System.Object
+{
+ .method private hidebysig specialname rtspecialname instance void .ctor(
+ valuetype ICSharpCode.Decompiler.Tests.TestCases.ILPretty.SpilledRole role,
+ string content,
+ [in] valuetype ICSharpCode.Decompiler.Tests.TestCases.ILPretty.SpilledPatch& patch,
+ string refusal,
+ string participantName,
+ object output,
+ object tools,
+ string function
+ ) cil managed
+ {
+ .param [3]
+ .custom instance void [System.Runtime]System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = (01 00 00 00)
+ .maxstack 8
+ IL_0000: ldarg.0
+ IL_0001: call instance void [System.Runtime]System.Object::.ctor()
+ IL_0006: ret
+ }
+
+ .method public hidebysig specialname rtspecialname instance void .ctor(
+ class ICSharpCode.Decompiler.Tests.TestCases.ILPretty.SpilledArgumentSource source
+ ) cil managed
+ {
+ .maxstack 9
+ .locals init (
+ [0] string function,
+ [1] valuetype ICSharpCode.Decompiler.Tests.TestCases.ILPretty.SpilledPatch patch
+ )
+
+ IL_0000: ldarg.0
+ IL_0001: ldc.i4.2
+ IL_0002: ldarg.1
+ IL_0003: brtrue.s IL_0008
+ IL_0005: ldnull
+ IL_0006: br.s IL_000e
+ IL_0008: ldarg.1
+ IL_0009: ldfld string ICSharpCode.Decompiler.Tests.TestCases.ILPretty.SpilledArgumentSource::Content
+ IL_000e: ldloca.s patch
+ IL_0010: dup
+ IL_0011: initobj ICSharpCode.Decompiler.Tests.TestCases.ILPretty.SpilledPatch
+ IL_0017: ldarg.1
+ IL_0018: brtrue.s IL_001d
+ IL_001a: ldnull
+ IL_001b: br.s IL_0023
+ IL_001d: ldarg.1
+ IL_001e: ldfld string ICSharpCode.Decompiler.Tests.TestCases.ILPretty.SpilledArgumentSource::Refusal
+ IL_0023: ldnull
+ IL_0024: ldarg.1
+ IL_0025: brtrue.s IL_002a
+ IL_0027: ldnull
+ IL_0028: br.s IL_0030
+ IL_002a: ldarg.1
+ IL_002b: ldfld string ICSharpCode.Decompiler.Tests.TestCases.ILPretty.SpilledArgumentSource::Function
+ IL_0030: stloc.0
+ IL_0031: ldarg.1
+ IL_0032: brtrue.s IL_0037
+ IL_0034: ldnull
+ IL_0035: br.s IL_003d
+ IL_0037: ldarg.1
+ IL_0038: ldfld object ICSharpCode.Decompiler.Tests.TestCases.ILPretty.SpilledArgumentSource::Output
+ IL_003d: brtrue.s IL_0042
+ IL_003f: ldnull
+ IL_0040: br.s IL_0047
+ IL_0042: newobj instance void [System.Runtime]System.Object::.ctor()
+ IL_0047: ldnull
+ IL_0048: ldloc.0
+ IL_0049: call instance void ICSharpCode.Decompiler.Tests.TestCases.ILPretty.SpilledConstructorInitializer::.ctor(
+ valuetype ICSharpCode.Decompiler.Tests.TestCases.ILPretty.SpilledRole,
+ string,
+ valuetype ICSharpCode.Decompiler.Tests.TestCases.ILPretty.SpilledPatch&,
+ string,
+ string,
+ object,
+ object,
+ string
+ )
+ IL_004e: ret
+ }
+}
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/MissingBaseConstructor.cs b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/MissingBaseConstructor.cs
new file mode 100644
index 0000000000..794871ecef
--- /dev/null
+++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/MissingBaseConstructor.cs
@@ -0,0 +1,10 @@
+namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty
+{
+ public class MissingBaseConstructor : MissingBase
+ {
+ public MissingBaseConstructor(MissingRole role, string content)
+ : base(role, content)
+ {
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/MissingBaseConstructor.il b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/MissingBaseConstructor.il
new file mode 100644
index 0000000000..0c01dbc249
--- /dev/null
+++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/MissingBaseConstructor.il
@@ -0,0 +1,36 @@
+.assembly extern System.Runtime
+{
+ .publickeytoken = (B7 7A 5C 56 19 34 E0 89)
+ .ver 4:0:0:0
+}
+
+.assembly extern MissingBaseAssembly
+{
+ .ver 1:0:0:0
+}
+
+.assembly MissingBaseConstructor
+{
+ .hash algorithm 0x00008004
+ .ver 0:0:0:0
+}
+
+.class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.ILPretty.MissingBaseConstructor
+ extends [MissingBaseAssembly]MissingBase
+{
+ .method public hidebysig specialname rtspecialname instance void .ctor(
+ valuetype [MissingBaseAssembly]MissingRole role,
+ string content
+ ) cil managed
+ {
+ .maxstack 8
+ IL_0000: ldarg.0
+ IL_0001: ldarg.1
+ IL_0002: ldarg.2
+ IL_0003: call instance void [MissingBaseAssembly]MissingBase::.ctor(
+ valuetype [MissingBaseAssembly]MissingRole,
+ string
+ )
+ IL_0008: ret
+ }
+}
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/GlobalNamespaceDocument.cs b/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/GlobalNamespaceDocument.cs
new file mode 100644
index 0000000000..9b4eba89e8
--- /dev/null
+++ b/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/GlobalNamespaceDocument.cs
@@ -0,0 +1,4 @@
+public class GlobalNamespaceDocument
+{
+ public int Value => 1;
+}
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CachedReadOnlySpanInitialization.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CachedReadOnlySpanInitialization.cs
index 18839d4607..dd0a8d9778 100644
--- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CachedReadOnlySpanInitialization.cs
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CachedReadOnlySpanInitialization.cs
@@ -8,7 +8,9 @@ public static class CachedReadOnlySpanInitialization
// compiler-generated lazy cache in for a ReadOnlySpan
// created from a multi-byte array literal. The CachedReadOnlySpanInitialization transform
// collapses that cache back to the explicit ReadOnlySpan constructor.
-#if NET70
+#if EXPECTED_OUTPUT && CS120
+ public static ReadOnlySpan NewLine => ['\r', '\n'];
+#elif NET70
public static ReadOnlySpan NewLine => new char[2] { '\r', '\n' };
#else
public static ReadOnlySpan NewLine => new ReadOnlySpan(new char[2] { '\r', '\n' });
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CollectionExpressions.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CollectionExpressions.cs
new file mode 100644
index 0000000000..c7bd511059
--- /dev/null
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CollectionExpressions.cs
@@ -0,0 +1,130 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+
+namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
+{
+ [CollectionBuilder(typeof(BuilderCollectionFactory), "Create")]
+ public sealed class BuilderCollection : IEnumerable, IEnumerable
+ {
+ private readonly int[] items;
+
+ public BuilderCollection(int[] items)
+ {
+ this.items = items;
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ return ((IEnumerable)items).GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return items.GetEnumerator();
+ }
+ }
+
+ public static class BuilderCollectionFactory
+ {
+ public static BuilderCollection Create(ReadOnlySpan items)
+ {
+ return new BuilderCollection(items.ToArray());
+ }
+ }
+
+ public static class CollectionExpressions
+ {
+ public static int[] EmptyArray()
+ {
+ return [];
+ }
+
+ public static int[] ArrayElements()
+ {
+ return [1, 2, 3];
+ }
+
+ public static int[] ArraySpread(IEnumerable items)
+ {
+ return [0, .. items, 4];
+ }
+
+ public static List EmptyList()
+ {
+ return [];
+ }
+
+ public static List ListElements()
+ {
+ return [1, 2, 3];
+ }
+
+ public static List ListSpread(IEnumerable first, int[] second)
+ {
+ return [0, .. first, 1, .. second, 2];
+ }
+
+ public static IList InterfaceElements()
+ {
+ return [1, 2, 3];
+ }
+
+ public static IEnumerable EnumerableElements()
+ {
+ return [1, 2, 3];
+ }
+
+ public static int SpanElements()
+ {
+#if EXPECTED_OUTPUT
+#if ROSLYN5
+ Span inlineArray = [1, 2, 3];
+ return inlineArray[0];
+#else
+ Span obj = [1, 2, 3];
+ return obj[0];
+#endif
+#else
+ Span span = [1, 2, 3];
+ return span[0];
+#endif
+ }
+
+ public static int ReadOnlySpanElements()
+ {
+#if EXPECTED_OUTPUT
+ return ((ReadOnlySpan)[1, 2, 3])[0];
+#else
+ ReadOnlySpan span = [1, 2, 3];
+ return span[0];
+#endif
+ }
+
+ public static BuilderCollection BuilderElements()
+ {
+ return [1, 2, 3];
+ }
+
+ public static BuilderCollection BuilderSpread(IEnumerable items)
+ {
+ return [0, .. items, 4];
+ }
+
+ public static int[,] MultiDimensionalArray()
+ {
+ return new int[2, 3] {
+ { 0, 1, 2 },
+ { 3, 4, 5 }
+ };
+ }
+
+ public static List