File: UseIndexOperatorTests.cs
Web Access
Project: ..\..\..\src\CodeStyle\CSharp\Tests\Microsoft.CodeAnalysis.CSharp.CodeStyle.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.CodeStyle.UnitTests)
// 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<
        CSharpUseIndexOperatorDiagnosticAnalyzer,
        CSharpUseIndexOperatorCodeFixProvider>;
 
    [Trait(Traits.Feature, Traits.Features.CodeActionsUseIndexOperator)]
    public class UseIndexOperatorTests
    {
        [Fact]
        public async Task TestNotInCSharp7()
        {
            var source =
                """
                class C
                {
                    void Goo(string s)
                    {
                        var v = s[s.Length - 1];
                    }
                }
                """;
 
            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = source,
                LanguageVersion = LanguageVersion.CSharp7,
            }.RunAsync();
        }
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseRangeOperator)]
        public async Task TestWithMissingReference()
        {
            var source =
                """
                class {|#0:C|}
                {
                    {|#1:void|} Goo({|#2:string|} s)
                    {
                        var v = s[s.Length - {|#3: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,30): error CS0518: Predefined type 'System.Int32' is not defined or imported
                    DiagnosticResult.CompilerError("CS0518").WithLocation(3).WithArguments("System.Int32"),
                },
                FixedCode = source,
            }.RunAsync();
        }
 
        [Fact]
        public async Task TestSimple()
        {
            var source =
                """
                class C
                {
                    void Goo(string s)
                    {
                        var v = s[[|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]
        public async Task TestMultipleDefinitions()
        {
            var source =
                """
                class C
                {
                    void Goo(string s)
                    {
                        var v = s[[|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 TestComplexSubtaction()
        {
            var source =
                """
                class C
                {
                    void Goo(string s)
                    {
                        var v = s[[|s.Length - (1 + 1)|]];
                    }
                }
                """;
            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 TestComplexInstance()
        {
            var source =
                """
                using System.Linq;
 
                class C
                {
                    void Goo(string[] ss)
                    {
                        var v = ss.Last()[[|ss.Last().Length - 3|]];
                    }
                }
                """;
            var fixedSource =
                """
                using System.Linq;
 
                class C
                {
                    void Goo(string[] ss)
                    {
                        var v = ss.Last()[^3];
                    }
                }
                """;
 
            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = fixedSource,
            }.RunAsync();
        }
 
        [Fact]
        public async Task TestNotWithoutSubtraction1()
        {
            var source =
                """
                class C
                {
                    void Goo(string s)
                    {
                        var v = s[s.Length];
                    }
                }
                """;
 
            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = source,
            }.RunAsync();
        }
 
        [Fact]
        public async Task TestNotWithoutSubtraction2()
        {
            var source =
                """
                class C
                {
                    void Goo(string s)
                    {
                        var v = s[s.Length + 1];
                    }
                }
                """;
 
            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = source,
            }.RunAsync();
        }
 
        [Fact]
        public async Task TestNotWithMultipleArgs()
        {
            var source =
                """
                struct S { public int Length { get; } public int this[int i] { get => 0; } public int this[int i, int j] { get => 0; } public int this[System.Index i] { get => 0; } }
                class C
                {
                    void Goo(S s)
                    {
                        var v = s[s.Length - 1, 2];
                    }
                }
                """;
 
            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = source,
            }.RunAsync();
        }
 
        [Fact]
        public async Task TestUserDefinedTypeWithLength()
        {
            var source =
                """
                struct S { public int Length { get; } public int this[int i] { get => 0; } public int this[System.Index i] { get => 0; } }
                class C
                {
                    void Goo(S s)
                    {
                        var v = s[[|s.Length - 2|]];
                    }
                }
                """;
            var fixedSource =
                """
                struct S { public int Length { get; } public int this[int i] { get => 0; } public int this[System.Index i] { get => 0; } }
                class C
                {
                    void Goo(S s)
                    {
                        var v = s[^2];
                    }
                }
                """;
 
            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = fixedSource,
            }.RunAsync();
        }
 
        [Fact]
        public async Task TestUserDefinedTypeWithCount()
        {
            var source =
                """
                struct S { public int Count { get; } public int this[int i] { get => 0; } public int this[System.Index i] { get => 0; } }
                class C
                {
                    void Goo(S s)
                    {
                        var v = s[[|s.Count - 2|]];
                    }
                }
                """;
            var fixedSource =
                """
                struct S { public int Count { get; } public int this[int i] { get => 0; } public int this[System.Index i] { get => 0; } }
                class C
                {
                    void Goo(S s)
                    {
                        var v = s[^2];
                    }
                }
                """;
 
            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = fixedSource,
            }.RunAsync();
        }
 
        [Fact]
        public async Task TestUserDefinedTypeWithNoLengthOrCount()
        {
            var source =
                """
                struct S { public int this[int i] { get => 0; } public int this[System.Index i] { get => 0; } }
                class C
                {
                    void Goo(S s)
                    {
                        var v = s[s.{|CS1061:Count|} - 2];
                    }
                }
                """;
 
            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = source,
            }.RunAsync();
        }
 
        [Fact]
        public async Task TestUserDefinedTypeWithNoInt32Indexer()
        {
            var source =
                """
                struct S { public int Length { get; } public int this[System.Index i] { get => 0; } }
                class C
                {
                    void Goo(S s)
                    {
                        var v = s[s.Length - 2];
                    }
                }
                """;
 
            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = source,
            }.RunAsync();
        }
 
        [Fact]
        public async Task TestUserDefinedTypeWithNoIndexIndexer()
        {
            var source =
                """
                struct S { public int Count { get; } public int this[int i] { get => 0; } }
                class C
                {
                    void Goo(S s)
                    {
                        var v = s[[|s.Count - 2|]];
                    }
                }
                """;
            var fixedSource =
                """
                struct S { public int Count { get; } public int this[int i] { get => 0; } }
                class C
                {
                    void Goo(S s)
                    {
                        var v = s[^2];
                    }
                }
                """;
 
            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = fixedSource,
            }.RunAsync();
        }
 
        [Fact]
        public async Task TestMethodToMethod()
        {
            var source =
                """
                struct S { public int Length { get; } public int Get(int i) => 0; public int Get(System.Index i) => 0; }
                class C
                {
                    void Goo(S s)
                    {
                        var v = s.Get([|s.Length - 1|]);
                    }
                }
                """;
            var fixedSource =
                """
                struct S { public int Length { get; } public int Get(int i) => 0; public int Get(System.Index i) => 0; }
                class C
                {
                    void Goo(S s)
                    {
                        var v = s.Get(^1);
                    }
                }
                """;
 
            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = fixedSource,
            }.RunAsync();
        }
 
        [Fact]
        public async Task TestMethodToMethodMissingIndexIndexer()
        {
            var source =
                """
                struct S { public int Length { get; } public int Get(int i) => 0; }
                class C
                {
                    void Goo(S s)
                    {
                        var v = s.Get(s.Length - 1);
                    }
                }
                """;
 
            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = source,
            }.RunAsync();
        }
 
        [Fact]
        public async Task TestMethodToMethodWithIntIndexer()
        {
            var source =
                """
                struct S { public int Length { get; } public int Get(int i) => 0; public int this[int i] { get => 0; } }
                class C
                {
                    void Goo(S s)
                    {
                        var v = s.Get(s.Length - 1);
                    }
                }
                """;
 
            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = source,
            }.RunAsync();
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/36909")]
        public async Task TestMissingWithNoSystemIndex()
        {
            var source =
                """
                class C
                {
                    void Goo(string[] s)
                    {
                        var v = s[s.Length - 1];
                    }
                }
                """;
 
            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp20,
                TestCode = source,
                FixedCode = source,
                LanguageVersion = LanguageVersion.CSharp8,
            }.RunAsync();
        }
 
        [Fact]
        public async Task TestMissingWithInaccessibleSystemIndex()
        {
            var source =
                """
                class C
                {
                    void Goo(string[] s)
                    {
                        var v = s[s.Length - 1];
                    }
                }
                """;
 
            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp20,
                TestState =
                {
                    Sources = { source },
                    AdditionalProjects =
                    {
                        ["AdditionalProject"] =
                        {
                            Sources =
                            {
                                "namespace System { internal struct Index { } }"
                            }
                        }
                    },
                    AdditionalProjectReferences = { "AdditionalProject" },
                },
                FixedCode = source,
                LanguageVersion = LanguageVersion.CSharp8,
            }.RunAsync();
        }
 
        [Fact]
        public async Task TestArray()
        {
            var source =
                """
                class C
                {
                    void Goo(string[] s)
                    {
                        var v = s[[|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]
        public async Task TestFixAll1()
        {
            var source =
                """
                class C
                {
                    void Goo(string s)
                    {
                        var v1 = s[[|s.Length - 1|]];
                        var v2 = s[[|s.Length - 1|]];
                    }
                }
                """;
            var fixedSource =
                """
                class C
                {
                    void Goo(string s)
                    {
                        var v1 = s[^1];
                        var v2 = s[^1];
                    }
                }
                """;
 
            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = fixedSource,
            }.RunAsync();
        }
 
        [Fact]
        public async Task TestNestedFixAll1()
        {
            var source =
                """
                class C
                {
                    void Goo(string[] s)
                    {
                        var v1 = s[[|s.Length - 2|]][[|s[[|s.Length - 2|]].Length - 1|]];
                    }
                }
                """;
            var fixedSource =
                """
                class C
                {
                    void Goo(string[] s)
                    {
                        var v1 = s[^2][^1];
                    }
                }
                """;
 
            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = fixedSource,
            }.RunAsync();
        }
 
        [Fact]
        public async Task TestNestedFixAll2()
        {
            var source =
                """
                class C
                {
                    void Goo(string[] s)
                    {
                        var v1 = s[[|s.Length - 2|]][[|s[[|s.Length - 2|]].Length - 1|]];
                    }
                }
                """;
            var fixedSource =
                """
                class C
                {
                    void Goo(string[] s)
                    {
                        var v1 = s[^2][^1];
                    }
                }
                """;
 
            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = fixedSource,
            }.RunAsync();
        }
 
        [Fact]
        public async Task TestSimple_NoIndexIndexer_SupportsIntIndexer()
        {
            var source =
                """
                using System.Collections.Generic;
                class C
                {
                    void Goo(List<int> s)
                    {
                        var v = s[[|s.Count - 1|]];
                    }
                }
                """;
            var fixedSource =
                """
                using System.Collections.Generic;
                class C
                {
                    void Goo(List<int> s)
                    {
                        var v = s[^1];
                    }
                }
                """;
 
            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = fixedSource,
            }.RunAsync();
        }
 
        [Fact]
        public async Task TestSimple_NoIndexIndexer_SupportsIntIndexer_Set()
        {
            var source =
                """
                using System.Collections.Generic;
                class C
                {
                    void Goo(List<int> s)
                    {
                        s[[|s.Count - 1|]] = 1;
                    }
                }
                """;
            var fixedSource =
                """
                using System.Collections.Generic;
                class C
                {
                    void Goo(List<int> s)
                    {
                        s[^1] = 1;
                    }
                }
                """;
 
            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = fixedSource,
            }.RunAsync();
        }
 
        [Fact]
        public async Task NotOnConstructedIndexer()
        {
            var source =
                """
                using System.Collections.Generic;
                class C
                {
                    void Goo(Dictionary<int, string> s)
                    {
                        var v = s[s.Count - 1];
                    }
                }
                """;
 
            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = source,
            }.RunAsync();
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/49347")]
        public async Task TestNotInExpressionTree()
        {
            var source =
                """
                using System;
                using System.Collections.Generic;
                using System.Linq.Expressions;
                class C
                {
                    void Goo(List<int> s)
                    {
                        Expression<Func<int>> f = () => s[s.Count - 1];
                    }
                }
                """;
 
            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31,
                TestCode = source,
                FixedCode = source,
            }.RunAsync();
        }
    }
}