diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs index 634aaa5cf..82d29611d 100644 --- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs @@ -800,6 +800,19 @@ namespace ICSharpCode.Decompiler.Tests await RunForLibrary(cscOptions: cscOptions); } + [Test] + public async Task FirstClassSpanTypes([ValueSource(nameof(roslyn5OrNewerOptions))] CompilerOptions cscOptions) + { + await RunForLibrary(cscOptions: cscOptions); + } + + [Test] + public async Task FirstClassSpanConversions([ValueSource(nameof(roslyn5OrNewerOptions))] CompilerOptions cscOptions) + { + Assert.Ignore("Implicit span conversions (C# 14 first-class span types) are not yet folded by the decompiler. See https://github.com/icsharpcode/ILSpy/issues/829"); + await RunForLibrary(cscOptions: cscOptions); + } + [Test] public async Task ExpandParamsArgumentsDisabled([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/FirstClassSpanConversions.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/FirstClassSpanConversions.cs new file mode 100644 index 000000000..8888496bd --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/FirstClassSpanConversions.cs @@ -0,0 +1,103 @@ +// 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; + +namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty +{ + internal static class FirstClassSpanConversions + { + internal class Base + { + } + + internal class Derived : Base + { + } + + public static void AcceptReadOnlySpanChar(ReadOnlySpan s) + { + } + + public static void AcceptReadOnlySpanBase(ReadOnlySpan s) + { + } + + public static void AcceptInReadOnlySpan(in ReadOnlySpan s) + { + } + + public static void ObjectOrReadOnlySpanChar(object a) + { + } + + public static void ObjectOrReadOnlySpanChar(ReadOnlySpan a) + { + } + + public static void StringArgument(string s) + { + AcceptReadOnlySpanChar(s); + ObjectOrReadOnlySpanChar(s); + s.ExtensionOnReadOnlySpanChar(); + } + + public static ReadOnlySpan StringToReadOnlySpanCharReturn(string s) + { + return s; + } + + public static int StringToReadOnlySpanCharLocal(string s) + { + ReadOnlySpan readOnlySpan = s; + return readOnlySpan.Length; + } + + public static void VarianceReadOnlySpan(ReadOnlySpan s) + { + AcceptReadOnlySpanBase(s); + } + + public static void VarianceSpan(Span s) + { + AcceptReadOnlySpanBase(s); + } + + public static ReadOnlySpan VarianceReturn(ReadOnlySpan s) + { + return s; + } + + public static void CovariantArrayToReadOnlySpan(Derived[] a) + { + AcceptReadOnlySpanBase(a); + } + + public static void InArgument(int[] a) + { + AcceptInReadOnlySpan(a); + } + } + + internal static class FirstClassSpanConversionsExtensions + { + public static void ExtensionOnReadOnlySpanChar(this ReadOnlySpan s) + { + } + } +} diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/FirstClassSpanTypes.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/FirstClassSpanTypes.cs new file mode 100644 index 000000000..82058e290 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/FirstClassSpanTypes.cs @@ -0,0 +1,186 @@ +// 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.Linq; + +namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty +{ + internal static class FirstClassSpanTypes + { + public static void ArrayOrReadOnlySpan(int[] a) + { + } + + public static void ArrayOrReadOnlySpan(ReadOnlySpan a) + { + } + + public static void ArrayOrSpan(int[] a) + { + } + + public static void ArrayOrSpan(Span a) + { + } + + public static void SpanOrReadOnlySpan(Span a) + { + } + + public static void SpanOrReadOnlySpan(ReadOnlySpan a) + { + } + + public static void ObjectOrReadOnlySpan(object a) + { + } + + public static void ObjectOrReadOnlySpan(ReadOnlySpan a) + { + } + + public static void ObjectOrReadOnlySpanChar(object a) + { + } + + public static void ObjectOrReadOnlySpanChar(ReadOnlySpan a) + { + } + + public static void EnumerableOrReadOnlySpan(IEnumerable a) + { + } + + public static void EnumerableOrReadOnlySpan(ReadOnlySpan a) + { + } + + public static void CovariantArrayOrReadOnlySpan(object[] a) + { + } + + public static void CovariantArrayOrReadOnlySpan(ReadOnlySpan a) + { + } + + public static void ReadOnlySpanOfObjectOrString(ReadOnlySpan a) + { + } + + public static void ReadOnlySpanOfObjectOrString(ReadOnlySpan a) + { + } + + public static void StringOrReadOnlySpanChar(string a) + { + } + + public static void StringOrReadOnlySpanChar(ReadOnlySpan a) + { + } + + public static void ParamsArrayOrParamsReadOnlySpan(params int[] a) + { + } + + public static void ParamsArrayOrParamsReadOnlySpan(params ReadOnlySpan a) + { + } + + public static void GenericArrayOrReadOnlySpan(T[] a) + { + } + + public static void GenericArrayOrReadOnlySpan(ReadOnlySpan a) + { + } + + public static void InferFromReadOnlySpan(ReadOnlySpan a) + { + } + + public static ReadOnlySpan ArrayToReadOnlySpanReturn(int[] a) + { + return a; + } + + public static ReadOnlySpan TernaryArrayOrSpan(bool b, int[] a, Span s) + { +#if OPT + if (!b) + { + return s; + } + return a; +#else + return b ? ((ReadOnlySpan)a) : ((ReadOnlySpan)s); +#endif + } + + public static bool SpanExtensionContains(int[] a) + { + // binds to MemoryExtensions.Contains under C# 14 first-class span conversions + return a.Contains(2); + } + + public static bool LinqContains(int[] a) + { + // Enumerable.Contains loses against MemoryExtensions.Contains under C# 14; + // extension method syntax must not be used here + return Enumerable.Contains(a, 2); + } + + public static void CallWinners(int[] arr, Span span, string str, string[] strArr) + { + ArrayOrReadOnlySpan(arr); + ArrayOrSpan(arr); + SpanOrReadOnlySpan(arr); + SpanOrReadOnlySpan(span); + ObjectOrReadOnlySpan(arr); + EnumerableOrReadOnlySpan(arr); + CovariantArrayOrReadOnlySpan(strArr); + ReadOnlySpanOfObjectOrString(strArr); + StringOrReadOnlySpanChar(str); + ParamsArrayOrParamsReadOnlySpan(arr); + GenericArrayOrReadOnlySpan(arr); + InferFromReadOnlySpan(arr); + InferFromReadOnlySpan(span); + arr.ExtensionOnReadOnlySpan(); + } + + public static void CallLosersWithExplicitConversions(int[] arr, string str) + { + ArrayOrReadOnlySpan((ReadOnlySpan)arr); + ArrayOrSpan((Span)arr); + SpanOrReadOnlySpan((Span)arr); + ObjectOrReadOnlySpan((object)arr); + ObjectOrReadOnlySpanChar((object)str); + EnumerableOrReadOnlySpan((IEnumerable)arr); + StringOrReadOnlySpanChar(str.AsSpan()); + } + } + + internal static class FirstClassSpanTypesExtensions + { + public static void ExtensionOnReadOnlySpan(this ReadOnlySpan s) + { + } + } +}