diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs index 8db635146..5bb191410 100644 --- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs @@ -788,6 +788,12 @@ namespace ICSharpCode.Decompiler.Tests await RunForLibrary(cscOptions: cscOptions); } + [Test] + public async Task RefStructInterfaces([ValueSource(nameof(roslyn4OrNewerOptions))] CompilerOptions cscOptions) + { + await RunForLibrary(cscOptions: cscOptions); + } + [Test] public async Task ExpandParamsArgumentsDisabled([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefStructInterfaces.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefStructInterfaces.cs new file mode 100644 index 000000000..d8560f4ae --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefStructInterfaces.cs @@ -0,0 +1,256 @@ +// Copyright (c) 2026 Siegfried Pammer +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Threading.Tasks; + +namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty +{ + public class RefStructInterfaces + { + public interface ICounter + { + int Value { get; } + + void Increment(); + } + + public interface IWithDefaultMethod + { + void Required(); + + void Optional() + { + Console.WriteLine("default"); + } + } + + public interface IStaticFactory where T : allows ref struct + { + static abstract T Create(int size); + } + + public interface IProcessor where T : allows ref struct + { + void Process(scoped T item); + } + + public interface IGenericMethods + { + void Run(scoped T value) where T : allows ref struct; + } + + public ref struct RefCounter : ICounter + { + private int count; + + public int Value => count; + + public void Increment() + { + count++; + } + } + + public ref struct ExplicitCounter : ICounter + { + private int count; + + int ICounter.Value => count; + + void ICounter.Increment() + { + count++; + } + } + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public ref struct DefaultMethodImpl : IWithDefaultMethod + { + public void Required() + { + } + + public void Optional() + { + Console.WriteLine("overridden"); + } + } + + public ref struct SpanFactory : IStaticFactory + { + public Span Buffer; + + public static SpanFactory Create(int size) + { + return default(SpanFactory); + } + } + + public ref struct Processor : IProcessor where T : allows ref struct + { + public T Item; + + public void Process(scoped T item) + { + } + } + + public abstract class BaseWithVirtual + { + public abstract void Process(T value) where T : allows ref struct; + + public virtual T Passthrough(T value) where T : IDisposable, allows ref struct + { + return value; + } + } + + public class DerivedOverride : BaseWithVirtual + { + public override void Process(T value) + { + } + + public override T Passthrough(T value) + { + return value; + } + } + + public class ExplicitGenericImpl : IGenericMethods + { + void IGenericMethods.Run(scoped T value) + { + } + } + + public class GenericHolder where T : allows ref struct + { + public void M() where U : allows ref struct + { + } + } + + public delegate void RefStructAction(T arg) where T : allows ref struct; + + public static void PlainAllows() where T : allows ref struct + { + } + + public static void UseCounter(T counter) where T : ICounter, allows ref struct + { + counter.Increment(); + Console.WriteLine(counter.Value); + } + + public static void UseDefaultMethod(T value) where T : IWithDefaultMethod, allows ref struct + { + value.Required(); + value.Optional(); + } + + public static void UsingOnT(T disposable) where T : IDisposable, allows ref struct + { + using (disposable) + { + Console.WriteLine("in using"); + } + } + + public static T CreateViaFactory(int size) where T : IStaticFactory, allows ref struct + { + return T.Create(size); + } + + public static T CreateDefault() where T : allows ref struct + { + return default(T); + } + + public static void CombinedUnmanaged() where T : unmanaged, allows ref struct + { + } + + public static void CombinedStruct() where T : struct, allows ref struct + { + } + + public static void CombinedNew() where T : new(), allows ref struct + { + } + + public static void ScopedAndRef(scoped T value, ref T byRef, in T input, out T output) where T : allows ref struct + { + output = byRef; + } + + public static void InvokeDelegate(RefStructAction> action) + { + action(default(Span)); + } + + public static void LocalFunctionAllows() + { + Local>(); + static void Local() where T : allows ref struct + { + } + } + + public static void CapturingLocalFunction(int seed) where T : allows ref struct + { + Nested(default(T)); + Console.WriteLine(seed); + void Nested(scoped T value) + { + seed++; + } + } + + public static IEnumerable Iterator() where T : allows ref struct + { + yield return 1; + yield return 2; + } + + public static async Task AsyncMethod() where T : allows ref struct + { + await Task.Delay(1); + return 42; + } + + public static void CallSites() + { + RefStructInterfaces.PlainAllows>(); + RefStructInterfaces.PlainAllows>(); + PlainAllows(); + RefStructInterfaces.UseCounter(default(RefCounter)); + RefStructInterfaces.UseCounter(default(ExplicitCounter)); + RefStructInterfaces.UseDefaultMethod(default(DefaultMethodImpl)); + new GenericHolder>().M>(); + } + + public static SpanFactory CallFactory() + { + return RefStructInterfaces.CreateViaFactory(16); + } + } +}