// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.UseIndexOrRangeOperator;
using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Testing;
using Roslyn.Test.Utilities;
using Xunit;

namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseIndexOrRangeOperator
{
    using VerifyCS = CSharpCodeFixVerifier<
        CSharpUseRangeOperatorDiagnosticAnalyzer,
        CSharpUseRangeOperatorCodeFixProvider>;

    [Trait(Traits.Feature, Traits.Features.CodeActionsUseRangeOperator)]
    public class UseRangeOperatorTests
    {
        [Fact]
        public async Task TestNotInCSharp7()
        {
            var source =
                """
                class C
                {
                    void Goo(string s)
                    {
                        var v = s.Substring(1, s.Length - 1);
                    }
                }
                """;

            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = source,
                LanguageVersion = LanguageVersion.CSharp7,
            }.RunAsync();
        }

        [Fact]
        public async Task TestWithMissingReference()
        {
            var source =
                """
                class {|#0:C|}
                {
                    {|#1:void|} Goo({|#2:string|} s)
                    {
                        var v = s.Substring({|#3:1|}, s.Length - {|#4:1|});
                    }
                }
                """;

            await new VerifyCS.Test
            {
                ReferenceAssemblies = new ReferenceAssemblies("custom"),
                TestCode = source,
                ExpectedDiagnostics =
                {
                    // /0/Test0.cs(1,7): error CS0518: Predefined type 'System.Object' is not defined or imported
                    DiagnosticResult.CompilerError("CS0518").WithLocation(0).WithArguments("System.Object"),
                    // /0/Test0.cs(1,7): error CS1729: 'object' does not contain a constructor that takes 0 arguments
                    DiagnosticResult.CompilerError("CS1729").WithLocation(0).WithArguments("object", "0"),
                    // /0/Test0.cs(3,5): error CS0518: Predefined type 'System.Void' is not defined or imported
                    DiagnosticResult.CompilerError("CS0518").WithLocation(1).WithArguments("System.Void"),
                    // /0/Test0.cs(3,14): error CS0518: Predefined type 'System.String' is not defined or imported
                    DiagnosticResult.CompilerError("CS0518").WithLocation(2).WithArguments("System.String"),
                    // /0/Test0.cs(5,29): error CS0518: Predefined type 'System.Int32' is not defined or imported
                    DiagnosticResult.CompilerError("CS0518").WithLocation(3).WithArguments("System.Int32"),
                    // /0/Test0.cs(5,43): error CS0518: Predefined type 'System.Int32' is not defined or imported
                    DiagnosticResult.CompilerError("CS0518").WithLocation(4).WithArguments("System.Int32"),
                },
                FixedCode = source,
            }.RunAsync();
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/36909")]
        public async Task TestNotWithoutSystemRange()
        {
            var source =
                """
                class C
                {
                    void Goo(string s)
                    {
                        var v = s.Substring(1, s.Length - 1);
                    }
                }
                """;

            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp20,
                TestCode = source,
                FixedCode = source,
                LanguageVersion = LanguageVersion.CSharp8,
            }.RunAsync();
        }

        [Fact]
        public async Task TestNotWithInaccessibleSystemRange()
        {
            var source =
                """
                class C
                {
                    void Goo(string s)
                    {
                        var v = s.Substring(1, s.Length - 1);
                    }
                }
                """;

            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp20,
                TestState =
                {
                    Sources = { source },
                    AdditionalProjects =
                    {
                        ["AdditionalProject"] =
                        {
                            Sources =
                            {
                                "namespace System { internal struct Range { } }"
                            }
                        }
                    },
                    AdditionalProjectReferences = { "AdditionalProject" },
                },
                FixedCode = source,
                LanguageVersion = LanguageVersion.CSharp8,
            }.RunAsync();
        }

        [Fact]
        public async Task TestSimple()
        {
            var source =
                """
                class C
                {
                    void Goo(string s)
                    {
                        var v = s.Substring([|1, s.Length - 1|]);
                    }
                }
                """;
            var fixedSource =
                """
                class C
                {
                    void Goo(string s)
                    {
                        var v = s[1..];
                    }
                }
                """;

            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = fixedSource,
            }.RunAsync();
        }

        [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseIndexOperator)]
        public async Task TestMultipleDefinitions()
        {
            var source =
                """
                class C
                {
                    void Goo(string s)
                    {
                        var v = s.Substring([|1, s.Length - 1|]);
                    }
                }
                """;
            var fixedSource =
                """
                class C
                {
                    void Goo(string s)
                    {
                        var v = s[1..];
                    }
                }
                """;

            // Adding a dependency with internal definitions of Index and Range should not break the feature
            var source1 = "namespace System { internal struct Index { } internal struct Range { } }";

            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestState =
                {
                    Sources = { source },
                    AdditionalProjects =
                    {
                        ["DependencyProject"] =
                        {
                            ReferenceAssemblies = ReferenceAssemblies.NetStandard.NetStandard20,
                            Sources = { source1 },
                        },
                    },
                    AdditionalProjectReferences = { "DependencyProject" },
                },
                FixedCode = fixedSource,
            }.RunAsync();
        }

        [Fact]
        public async Task TestComplexSubstraction()
        {
            var source =
                """
                class C
                {
                    void Goo(string s, int bar, int baz)
                    {
                        var v = s.Substring([|bar, s.Length - baz - bar|]);
                    }
                }
                """;
            var fixedSource =
                """
                class C
                {
                    void Goo(string s, int bar, int baz)
                    {
                        var v = s[bar..^baz];
                    }
                }
                """;

            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = fixedSource,
            }.RunAsync();
        }

        [Fact]
        public async Task TestSubstringOneArgument()
        {
            var source =
                """
                class C
                {
                    void Goo(string s)
                    {
                        var v = s.Substring([|1|]);
                    }
                }
                """;
            var fixedSource =
                """
                class C
                {
                    void Goo(string s)
                    {
                        var v = s[1..];
                    }
                }
                """;

            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = fixedSource,
            }.RunAsync();
        }

        [Fact]
        public async Task TestSliceOneArgument()
        {
            var source =
                """
                using System;
                class C
                {
                    void Goo(Span<int> s)
                    {
                        var v = s.Slice([|1|]);
                    }
                }
                """;
            var fixedSource =
                """
                using System;
                class C
                {
                    void Goo(Span<int> s)
                    {
                        var v = s[1..];
                    }
                }
                """;

            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = fixedSource,
            }.RunAsync();
        }

        [Fact]
        public async Task TestExpressionOneArgument()
        {
            var source =
                """
                class C
                {
                    void Goo(string s, int bar)
                    {
                        var v = s.Substring([|bar|]);
                    }
                }
                """;
            var fixedSource =
                """
                class C
                {
                    void Goo(string s, int bar)
                    {
                        var v = s[bar..];
                    }
                }
                """;

            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = fixedSource,
            }.RunAsync();
        }

        [Fact]
        public async Task TestConstantSubtraction1()
        {
            var source =
                """
                class C
                {
                    void Goo(string s)
                    {
                        var v = s.Substring([|1, s.Length - 2|]);
                    }
                }
                """;
            var fixedSource =
                """
                class C
                {
                    void Goo(string s)
                    {
                        var v = s[1..^1];
                    }
                }
                """;

            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = fixedSource,
            }.RunAsync();
        }

        [Fact]
        public async Task TestNotWithoutSubtraction()
        {
            var source =
                """
                class C
                {
                    void Goo(string s)
                    {
                        var v = s.Substring(1, 2);
                    }
                }
                """;

            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = source,
                LanguageVersion = LanguageVersion.CSharp7,
            }.RunAsync();
        }

        [Fact]
        public async Task TestNonStringType()
        {
            var source =
                """
                struct S { public S Slice(int start, int length) => default; public int Length { get; } public S this[System.Range r] { get => default; } }
                class C
                {
                    void Goo(S s)
                    {
                        var v = s.Slice([|1, s.Length - 2|]);
                    }
                }
                """;
            var fixedSource =
                """
                struct S { public S Slice(int start, int length) => default; public int Length { get; } public S this[System.Range r] { get => default; } }
                class C
                {
                    void Goo(S s)
                    {
                        var v = s[1..^1];
                    }
                }
                """;

            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = fixedSource,
            }.RunAsync();
        }

        [Fact]
        public async Task TestNonStringTypeWithoutRangeIndexer()
        {
            var source =
                """
                struct S { public S Slice(int start, int length) => default; public int Length { get; } }
                class C
                {
                    void Goo(S s)
                    {
                        var v = s.Slice([|1, s.Length - 2|]);
                    }
                }
                """;
            var fixedSource =
                """
                struct S { public S Slice(int start, int length) => default; public int Length { get; } }
                class C
                {
                    void Goo(S s)
                    {
                        var v = s[1..^1];
                    }
                }
                """;

            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = fixedSource,
            }.RunAsync();
        }

        [Fact]
        public async Task TestNonStringType_Assignment()
        {
            var source =
                """
                struct S { public ref S Slice(int start, int length) => throw null; public int Length { get; } public ref S this[System.Range r] { get => throw null; } }
                class C
                {
                    void Goo(S s)
                    {
                        s.Slice([|1, s.Length - 2|]) = default;
                    }
                }
                """;
            var fixedSource =
                """
                struct S { public ref S Slice(int start, int length) => throw null; public int Length { get; } public ref S this[System.Range r] { get => throw null; } }
                class C
                {
                    void Goo(S s)
                    {
                        s[1..^1] = default;
                    }
                }
                """;
            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = fixedSource,
            }.RunAsync();
        }

        [Fact]
        public async Task TestMethodToMethod()
        {
            var source =
                """
                struct S { public int Slice(int start, int length) => 0; public int Length { get; } public int Slice(System.Range r) => 0; }
                class C
                {
                    void Goo(S s)
                    {
                        var v = s.Slice([|1, s.Length - 2|]);
                    }
                }
                """;
            var fixedSource =
                """
                struct S { public int Slice(int start, int length) => 0; public int Length { get; } public int Slice(System.Range r) => 0; }
                class C
                {
                    void Goo(S s)
                    {
                        var v = s.Slice(1..^1);
                    }
                }
                """;

            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = fixedSource,
            }.RunAsync();
        }

        [Fact]
        public async Task TestFixAllInvocationToElementAccess1()
        {
            // Note: once the IOp tree has support for range operators, this should 
            // simplify even further.
            var source =
                """
                class C
                {
                    void Goo(string s, string t)
                    {
                        var v = t.Substring([|s.Substring([|1, s.Length - 2|])[0], t.Length - s.Substring([|1, s.Length - 2|])[0]|]);
                    }
                }
                """;
            var fixedSource =
                """
                class C
                {
                    void Goo(string s, string t)
                    {
                        var v = t[s[1..^1][0]..];
                    }
                }
                """;

            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = fixedSource,
            }.RunAsync();
        }

        [Fact]
        public async Task TestFixAllInvocationToElementAccess2()
        {
            // Note: once the IOp tree has support for range operators, this should 
            // simplify even further.
            var source =
                """
                class C
                {
                    void Goo(string s, string t)
                    {
                        var v = t.Substring([|s.Substring([|1|])[0]|]);
                    }
                }
                """;
            var fixedSource =
                """
                class C
                {
                    void Goo(string s, string t)
                    {
                        var v = t[s[1..][0]..];
                    }
                }
                """;

            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = fixedSource,
            }.RunAsync();
        }

        [Fact]
        public async Task TestWithTypeWithActualSliceMethod1()
        {
            var source =
                """
                using System;
                class C
                {
                    void Goo(Span<int> s)
                    {
                        var v = s.Slice([|1, s.Length - 1|]);
                    }
                }
                """;
            var fixedSource =
                """
                using System;
                class C
                {
                    void Goo(Span<int> s)
                    {
                        var v = s[1..];
                    }
                }
                """;

            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = fixedSource,
            }.RunAsync();
        }

        [Fact]
        public async Task TestWithTypeWithActualSliceMethod2()
        {
            var source =
                """
                using System;
                class C
                {
                    void Goo(Span<int> s)
                    {
                        var v = s.Slice([|1, s.Length - 2|]);
                    }
                }
                """;
            var fixedSource =
                """
                using System;
                class C
                {
                    void Goo(Span<int> s)
                    {
                        var v = s[1..^1];
                    }
                }
                """;

            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = fixedSource,
            }.RunAsync();
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/43202")]
        public async Task TestWritableType()
        {
            var source =
                """
                using System;
                struct S { 
                    public ref S Slice(int start, int length) => throw null; 
                    public int Length { get; } 
                    public S this[System.Range r] { get => default; } 
                }

                class C
                {
                    void Goo(S s)
                    {
                        s.Slice(1, s.Length - 2) = default;
                    }
                }
                """;

            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = source,
            }.RunAsync();
        }
        [Fact]
        public async Task TestReturnByRef()
        {
            var source =
                """
                struct S { public ref S Slice(int start, int length) => throw null; public int Length { get; } public S this[System.Range r] { get => throw null; } }
                class C
                {
                    void Goo(S s)
                    {
                        var x = s.Slice([|1, s.Length - 2|]);
                    }
                }
                """;
            var fixedSource =
                """
                struct S { public ref S Slice(int start, int length) => throw null; public int Length { get; } public S this[System.Range r] { get => throw null; } }
                class C
                {
                    void Goo(S s)
                    {
                        var x = s[1..^1];
                    }
                }
                """;
            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = fixedSource,
            }.RunAsync();
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/43202")]
        public async Task TestIntWritableType()
        {
            var source =
                """
                using System;
                struct S { 
                    public ref S Slice(int start, int length) => throw null;
                    public int Length { get; }
                    public S this[int r] { get => default; }
                }

                class C
                {
                    void Goo(S s)
                    {
                        s.Slice([|1, s.Length - 2|]) = default;
                    }
                }
                """;
            var fixedSource =
                """
                using System;
                struct S { 
                    public ref S Slice(int start, int length) => throw null;
                    public int Length { get; }
                    public S this[int r] { get => default; }
                }

                class C
                {
                    void Goo(S s)
                    {
                        s[1..^1] = default;
                    }
                }
                """;

            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = fixedSource,
            }.RunAsync();
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/43202")]
        public async Task TestReadWriteProperty()
        {
            var source =
                """
                using System;
                struct S { 
                    public ref S Slice(int start, int length) => throw null;
                    public int Length { get; }
                    public S this[System.Range r] { get => default; set { } }
                }

                class C
                {
                    void Goo(S s)
                    {
                        s.Slice([|1, s.Length - 2|]) = default;
                    }
                }
                """;
            var fixedSource =
                """
                using System;
                struct S { 
                    public ref S Slice(int start, int length) => throw null;
                    public int Length { get; }
                    public S this[System.Range r] { get => default; set { } }
                }

                class C
                {
                    void Goo(S s)
                    {
                        s[1..^1] = default;
                    }
                }
                """;
            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = fixedSource,
            }.RunAsync();
        }

        [Fact]
        public async Task TestWithTypeWithActualSliceMethod3()
        {
            var source =
                """
                using System;
                class C
                {
                    void Goo(Span<int> s)
                    {
                        var v = s.Slice([|1|]);
                    }
                }
                """;
            var fixedSource =
                """
                using System;
                class C
                {
                    void Goo(Span<int> s)
                    {
                        var v = s[1..];
                    }
                }
                """;
            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = fixedSource,
            }.RunAsync();
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/36997")]
        public async Task TestExpressionWithAddOperatorArgument()
        {
            var source =
                """
                class C
                {
                    void Goo(string s, int bar)
                    {
                        var v = s.Substring([|bar + 1|]);
                    }
                }
                """;
            var fixedSource =
                """
                class C
                {
                    void Goo(string s, int bar)
                    {
                        var v = s[(bar + 1)..];
                    }
                }
                """;

            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = fixedSource,
            }.RunAsync();
        }

        [Fact]
        public async Task TestExpressionWithElementAccessShouldNotAddParentheses()
        {
            var source =
                """
                class C
                {
                    void Goo(string s, int[] bar)
                    {
                        _ = s.Substring([|bar[0]|]);
                    }
                }
                """;
            var fixedSource =
                """
                class C
                {
                    void Goo(string s, int[] bar)
                    {
                        _ = s[bar[0]..];
                    }
                }
                """;

            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = fixedSource,
            }.RunAsync();
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/47183")]
        public async Task TestExpressionWithNullConditionalAccess()
        {
            var source =
                """
                #nullable enable
                public class Test
                {
                    public string? M(string? arg)
                        => arg?.Substring([|42|]);
                }
                """;
            var fixedSource =
                """
                #nullable enable
                public class Test
                {
                    public string? M(string? arg)
                        => arg?[42..];
                }
                """;
            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = fixedSource,
            }.RunAsync();
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/47183")]
        public async Task TestExpressionWithNullConditionalAccessWithPropertyAccess()
        {
            var source =
                """
                public class Test
                {
                    public int? M(string arg)
                        => arg?.Substring([|42|]).Length;
                }
                """;
            var fixedSource =
                """
                public class Test
                {
                    public int? M(string arg)
                        => arg?[42..].Length;
                }
                """;
            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = fixedSource,
            }.RunAsync();
        }

        [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/47183")]
        [InlineData(
            "c.Prop.Substring([|42|])",
            "c.Prop[42..]")]
        [InlineData(
            "c.Prop.Substring([|1, c.Prop.Length - 2|])",
            "c.Prop[1..^1]")]
        [InlineData(
            "c?.Prop.Substring([|42|])",
            "c?.Prop[42..]")]
        [InlineData(
            "c.Prop?.Substring([|42|])",
            "c.Prop?[42..]")]
        [InlineData(
            "c?.Prop?.Substring([|42|])",
            "c?.Prop?[42..]")]
        public async Task TestExpressionWithNullConditionalAccessVariations(string subStringCode, string rangeCode)
        {
            var source =
@$"
public class C
{{
    public string Prop {{ get; set; }}
}}
public class Test
{{
    public object M(C c)
        => {subStringCode};
}}";
            var fixedSource =
@$"
public class C
{{
    public string Prop {{ get; set; }}
}}
public class Test
{{
    public object M(C c)
        => {rangeCode};
}}";
            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = fixedSource,
            }.RunAsync();
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38055")]
        public async Task TestStringMethod()
        {
            var source =
                """
                namespace System
                {
                    public class Object {}
                    public class ValueType : Object {}
                    public struct Void {}
                    public struct Int32 {}
                    public struct Index
                    {
                        public int GetOffset(int length) => 0;
                        public static implicit operator Index(int value) => default;
                    }
                    public struct Range
                    {
                        public Range(Index start, Index end) {}
                        public Index Start => default;
                        public Index End => default;
                    }
                    public class String : Object
                    {
                        public int Length => 0;
                        public string Substring(int start, int length) => this;

                        string Foo(int x) => Substring([|1, x - 1|]);
                    }
                }
                """;
            var fixedSource =
                """
                namespace System
                {
                    public class Object {}
                    public class ValueType : Object {}
                    public struct Void {}
                    public struct Int32 {}
                    public struct Index
                    {
                        public int GetOffset(int length) => 0;
                        public static implicit operator Index(int value) => default;
                    }
                    public struct Range
                    {
                        public Range(Index start, Index end) {}
                        public Index Start => default;
                        public Index End => default;
                    }
                    public class String : Object
                    {
                        public int Length => 0;
                        public string Substring(int start, int length) => this;

                        string Foo(int x) => this[1..x];
                    }
                }
                """;

            await new VerifyCS.Test
            {
                ReferenceAssemblies = new ReferenceAssemblies("nostdlib"),
                TestCode = source,
                FixedCode = fixedSource,
            }.RunAsync();
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38055")]
        public async Task TestSliceOnThis()
        {
            var source =
                """
                class C
                {
                    public int Length => 0;
                    public C Slice(int start, int length) => this;

                    public C Foo(int x) => Slice([|1, x - 1|]);
                }
                """;
            var fixedSource =
                """
                class C
                {
                    public int Length => 0;
                    public C Slice(int start, int length) => this;

                    public C Foo(int x) => this[1..x];
                }
                """;

            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = fixedSource,
            }.RunAsync();
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56269")]
        public async Task TestStartingFromZero()
        {
            var source =
                """
                class C
                {
                    void Goo(string s)
                    {
                        var v = s.Substring([|0|]);
                    }
                }
                """;
            var fixedSource =
                """
                class C
                {
                    void Goo(string s)
                    {
                        var v = s[..];
                    }
                }
                """;

            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = fixedSource,
            }.RunAsync();
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56269")]
        public async Task TestStartingFromAribtraryPosition()
        {
            var source =
                """
                class C
                {
                    void Goo(string s)
                    {
                        var v = s.Substring([|5|]);
                    }
                }
                """;
            var fixedSource =
                """
                class C
                {
                    void Goo(string s)
                    {
                        var v = s[5..];
                    }
                }
                """;

            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = fixedSource,
            }.RunAsync();
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56269")]
        public async Task TestStartingFromZeroToArbitraryEnd()
        {
            var source =
                """
                class C
                {
                    void Goo(string s)
                    {
                        var v = s.Substring([|0, 5|]);
                    }
                }
                """;
            var fixedSource =
                """
                class C
                {
                    void Goo(string s)
                    {
                        var v = s[..5];
                    }
                }
                """;

            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = fixedSource,
            }.RunAsync();
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56269")]
        public async Task TestStartingFromZeroGoingToLength()
        {
            var source =
                """
                class C
                {
                    void Goo(string s)
                    {
                        var v = s.Substring([|0, s.Length|]);
                    }
                }
                """;
            var fixedSource =
                """
                class C
                {
                    void Goo(string s)
                    {
                        var v = s[..];
                    }
                }
                """;

            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = fixedSource,
            }.RunAsync();
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/40438")]
        public async Task TestStartingFromZeroGoingToLengthMinus1()
        {
            var source =
                """
                class C
                {
                    void Goo(string s)
                    {
                        var v = s.Substring([|0, s.Length - 1|]);
                    }
                }
                """;
            var fixedSource =
                """
                class C
                {
                    void Goo(string s)
                    {
                        var v = s[..^1];
                    }
                }
                """;

            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = fixedSource,
            }.RunAsync();
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/49347")]
        public async Task TestNotInExpressionTree()
        {
            var source =
                """
                using System;
                using System.Linq.Expressions;

                class C
                {
                    void M()
                    {
                        Expression<Func<string, int, string>> e = (s, i) => s.Substring(i);
                    }
                }
                """;

            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = source,
            }.RunAsync();
        }

        [Fact, WorkItem(60988, "https://github.com/dotnet/roslyn/issues/60988")]
        public async Task TestCheckedExpression()
        {
            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode =
                """
                using System;
                using System.Linq.Expressions;

                class C
                {
                    void M()
                    {
                        Span<byte> buffer = new byte[]{ (byte)'h', (byte)'i', 0 };
                        long length = 2;
                        var sliced = buffer.Slice([|0, unchecked((int)length)|]); // or checked((int)length)
                    }
                }
                """,
                FixedCode =
                """
                using System;
                using System.Linq.Expressions;

                class C
                {
                    void M()
                    {
                        Span<byte> buffer = new byte[]{ (byte)'h', (byte)'i', 0 };
                        long length = 2;
                        var sliced = buffer[..unchecked((int)length)]; // or checked((int)length)
                    }
                }
                """,
            }.RunAsync();
        }
    }
}
