File: ReassignedVariable\CSharpReassignedVariableTests.cs
Web Access
Project: src\src\EditorFeatures\CSharpTest\Microsoft.CodeAnalysis.CSharp.EditorFeatures.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.EditorFeatures.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.Editor.UnitTests.ReassignedVariable;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ReassignedVariable;
 
public sealed class CSharpReassignedVariableTests : AbstractReassignedVariableTests
{
    protected override EditorTestWorkspace CreateWorkspace(string markup)
        => EditorTestWorkspace.CreateCSharp(markup);
 
    [Fact]
    public Task TestNoParameterReassignment()
        => TestAsync(
            """
            class C
            {
                void M(int p)
                {
                }
            }
            """);
 
    [Fact]
    public Task TestParameterReassignment()
        => TestAsync(
            """
            class C
            {
                void M(int [|p|])
                {
                    [|p|] = 1;
                }
            }
            """);
 
    [Fact]
    public Task TestParameterReassignmentWhenReadAfter()
        => TestAsync(
            """
            using System;
            class C
            {
                void M(int [|p|])
                {
                    [|p|] = 1;
                    Console.WriteLine([|p|]);
                }
            }
            """);
 
    [Fact]
    public Task TestParameterReassignmentWhenReadBefore()
        => TestAsync(
            """
            using System;
            class C
            {
                void M(int [|p|])
                {
                    Console.WriteLine([|p|]);
                    [|p|] = 1;
                }
            }
            """);
 
    [Fact]
    public Task TestParameterReassignmentWhenReadWithDefaultValue()
        => TestAsync(
            """
            using System;
            class C
            {
                void M(int [|p|] = 1)
                {
                    Console.WriteLine([|p|]);
                    [|p|] = 1;
                }
            }
            """);
 
    [Fact]
    public Task TestParameterWithExprBodyWithReassignment()
        => TestAsync(
            """
            using System;
            class C
            {
                void M(int [|p|]) => Console.WriteLine([|p|]++);
            }
            """);
 
    [Fact]
    public Task TestLocalFunctionWithExprBodyWithReassignment()
        => TestAsync(
            """
            using System;
            class C
            {
                void M()
                {
                    void Local(int [|p|])
                        => Console.WriteLine([|p|]++);
            }
            """);
 
    [Fact]
    public Task TestIndexerWithWriteInExprBody()
        => TestAsync(
            """
            using System;
            class C
            {
                int this[int [|p|]] => [|p|]++;
            }
            """);
 
    [Fact]
    public Task TestIndexerWithWriteInGetter1()
        => TestAsync(
            """
            using System;
            class C
            {
                int this[int [|p|]] { get => [|p|]++; }
            }
            """);
 
    [Fact]
    public Task TestIndexerWithWriteInGetter2()
        => TestAsync(
            """
            using System;
            class C
            {
                int this[int [|p|]] { get { [|p|]++; } }
            }
            """);
 
    [Fact]
    public Task TestIndexerWithWriteInSetter1()
        => TestAsync(
            """
            using System;
            class C
            {
                int this[int [|p|]] { set => [|p|]++; }
            }
            """);
 
    [Fact]
    public Task TestIndexerWithWriteInSetter2()
        => TestAsync(
            """
            using System;
            class C
            {
                int this[int [|p|]] { set { [|p|]++; } }
            }
            """);
 
    [Fact]
    public Task TestPropertyWithAssignmentToValue1()
        => TestAsync(
            """
            using System;
            class C
            {
                int Goo { set => [|value|] = [|value|] + 1; }
            }
            """);
 
    [Fact]
    public Task TestPropertyWithAssignmentToValue2()
        => TestAsync(
            """
            using System;
            class C
            {
                int Goo { set { [|value|] = [|value|] + 1; } }
            }
            """);
 
    [Fact]
    public Task TestEventAddWithAssignmentToValue()
        => TestAsync(
            """
            using System;
            class C
            {
                event Action Goo { add { [|value|] = null; } remove { } }
            }
            """);
 
    [Fact]
    public Task TestEventRemoveWithAssignmentToValue()
        => TestAsync(
            """
            using System;
            class C
            {
                event Action Goo { add { } remove { [|value|] = null; } }
            }
            """);
 
    [Fact]
    public Task TestLambdaParameterWithoutReassignment()
        => TestAsync(
            """
            using System;
            class C
            {
                void M()
                {
                    Action<int> a = x => Console.WriteLine(x);
                }
            }
            """);
 
    [Fact]
    public Task TestLambdaParameterWithReassignment()
        => TestAsync(
            """
            using System;
            class C
            {
                void M()
                {
                    Action<int> a = [|x|] => Console.WriteLine([|x|]++);
                }
            }
            """);
 
    [Fact]
    public Task TestLambdaParameterWithReassignment2()
        => TestAsync(
            """
            using System;
            class C
            {
                void M()
                {
                    Action<int> a = (int [|x|]) => Console.WriteLine([|x|]++);
                }
            }
            """);
 
    [Fact]
    public Task TestLocalWithoutInitializerWithoutReassignment()
        => TestAsync(
            """
            using System;
            class C
            {
                void M(bool b)
                {
                    int p;
                    if (b)
                        p = 1;
                    else
                        p = 2;
 
                    Console.WriteLine(p);
                }
            }
            """);
 
    [Fact]
    public Task TestLocalWithoutInitializerWithReassignment()
        => TestAsync(
            """
            using System;
            class C
            {
                void M(bool b)
                {
                    int [|p|];
                    if (b)
                        [|p|] = 1;
                    else
                        [|p|] = 2;
 
                    [|p|] = 0;
                    Console.WriteLine([|p|]);
                }
            }
            """);
 
    [Fact]
    public Task TestLocalDeclaredByPattern()
        => TestAsync(
            """
            using System;
            class C
            {
                void M()
                {
                    if (0 is var [|p|]) [|p|] = 0;
                    Console.WriteLine([|p|]);
                }
            }
            """);
 
    [Fact]
    public Task TestLocalDeclaredByPatternButAssignedInFalseBranch()
        => TestAsync(
            """
            using System;
            class C
            {
                void M()
                {
                    if (0 is var [|p|])
                    {
                    }
                    else
                    {
                         [|p|] = 0;
                    }
 
                    Console.WriteLine([|p|]);
                }
            }
            """);
 
    [Fact]
    public Task TestLocalDeclaredByPositionalPattern()
        => TestAsync(
            """
            using System;
            class C
            {
                void M()
                {
                    if ((0, 1) is var ([|p|], _)) [|p|] = 0;
                    Console.WriteLine([|p|]);
                }
            }
            """);
 
    [Fact]
    public Task TestLocalDeclaredByOutVar()
        => TestAsync(
            """
            using System;
            class C
            {
                void M()
                {
                    M2(out var [|p|]);
                    [|p|] = 0;
                    Console.WriteLine([|p|]);
                }
 
                void M2(out int p) => p = 0;
            }
            """);
 
    [Fact]
    public Task TestOutParameterCausingReassignment()
        => TestAsync(
            """
            using System;
            class C
            {
                void M()
                {
                    int [|p|] = 0;
                    M2(out [|p|]);
                    Console.WriteLine([|p|]);
                }
 
                void M2(out int p) => p = 0;
            }
            """);
 
    [Fact]
    public Task TestOutParameterWithoutReassignment()
        => TestAsync(
            """
            using System;
            class C
            {
                void M()
                {
                    int p;
                    M2(out p);
                    Console.WriteLine(p);
                }
 
                void M2(out int p) => p = 0;
            }
            """);
 
    [Fact]
    public Task AssignmentThroughOutParameter()
        => TestAsync(
            """
            using System;
            class C
            {
                void M(out int [|p|])
                {
                    [|p|] = 0;
                    [|p|] = 1;
                    Console.WriteLine([|p|]);
                }
            }
            """);
 
    [Fact]
    public Task TestOutParameterReassignmentOneWrites()
        => TestAsync(
            """
            using System;
            class C
            {
                void M(out int p)
                {
                    p = ref p;
                    Console.WriteLine(p);
                }
            }
            """);
 
    [Fact]
    public Task AssignmentThroughRefParameter()
        => TestAsync(
            """
            using System;
            class C
            {
                void M(ref int [|p|])
                {
                    [|p|] = 0;
                    [|p|] = 1;
                    Console.WriteLine([|p|]);
                }
            }
            """);
 
    [Fact]
    public Task TestRefParameterReassignment()
        => TestAsync(
            """
            using System;
            class C
            {
                void M(ref int [|p|])
                {
                    [|p|] = ref [|p|];
                    Console.WriteLine([|p|]);
                }
            }
            """);
 
    [Fact]
    public Task AssignmentThroughRefLocal()
        => TestAsync(
            """
            using System;
            class C
            {
                void M(ref int [|p|])
                {
                    ref var [|local|] = ref [|p|];
                    [|local|] = 0;
                    [|local|] = 1;
                    Console.WriteLine([|local|]);
                }
            }
            """);
 
    [Fact]
    public Task AssignmentThroughScopedRefLocal()
        => TestAsync(
            """
            using System;
            class C
            {
                void M(ref int [|p|])
                {
                    scoped ref var [|local|] = ref [|p|];
                    [|local|] = 0;
                    [|local|] = 1;
                    Console.WriteLine([|local|]);
                }
            }
            """);
 
    [Fact]
    public Task TestRefLocalReassignment()
        => TestAsync(
            """
            using System;
            class C
            {
                void M(ref int [|p|])
                {
                    // p is statically detected as overwritten (even though it is not written at runtime)
                    // due to a limitation in alias analysis.
                    ref var [|local|] = ref [|p|];
                    [|local|] = ref [|p|];
                    Console.WriteLine([|local|]);
                }
            }
            """);
 
    [Fact]
    public Task AssignmentThroughPointerIsNotAssignmentOfTheVariableItself()
        => TestAsync(
            """
            using System;
            class C
            {
                unsafe void M(int* p)
                {
                    *p = 4;
                    Console.WriteLine((IntPtr)p);
                }
            }
            """);
 
    [Fact]
    public Task TestPointerVariableReassignment()
        => TestAsync(
            """
            using System;
            class C
            {
                unsafe void M(int* [|p|])
                {
                    [|p|] = null;
                    Console.WriteLine((IntPtr)[|p|]);
                }
            }
            """);
 
    [Fact]
    public Task TestRefParameterCausingPossibleReassignment()
        => TestAsync(
            """
            using System;
            class C
            {
                void M()
                {
                    int [|p|] = 0;
                    M2(ref [|p|]);
                    Console.WriteLine([|p|]);
                }
 
                void M2(ref int p) { }
            }
            """);
 
    [Fact]
    public Task TestVolatileRefReadParameterCausingPossibleReassignment()
        => TestAsync(
            """
            using System;
            using System.Threading;
            class C
            {
                void M()
                {
                    // p is statically detected as overwritten (even though it is not written at runtime)
                    // due to a limitation in ref analysis.
                    int [|p|] = 0;
                    Volatile.Read(ref [|p|]);
                    Console.WriteLine([|p|]);
                }
            }
            """);
 
    [Fact]
    public Task TestRefParameterWithoutReassignment()
        => TestAsync(
            """
            using System;
            class C
            {
                void M()
                {
                    int p;
                    M2(ref p);
                    Console.WriteLine(p);
                }
 
                void M2(ref int p) { }
            }
            """);
 
    [Fact]
    public Task TestRefLocalCausingPossibleReassignment()
        => TestAsync(
            """
            using System;
            class C
            {
                void M()
                {
                    int [|p|] = 0;
                    ref int refP = ref [|p|];
                    Console.WriteLine([|p|]);
                }
            }
            """);
 
    [Fact]
    public Task TestReadonlyRefLocalWithNoReassignment()
        => TestAsync(
            """
            using System;
            class C
            {
                void M()
                {
                    int p = 0;
                    ref readonly int refP = ref p;
                    Console.WriteLine(p);
                }
            }
            """);
 
    [Fact]
    public Task TestScopedReadonlyRefLocalWithNoReassignment()
        => TestAsync(
            """
            using System;
            class C
            {
                void M()
                {
                    int p = 0;
                    scoped ref readonly int refP = ref p;
                    Console.WriteLine(p);
                }
            }
            """);
 
    [Fact]
    public Task TestReadonlyRefLocalWithNoReassignment1()
        => TestAsync(
            """
            using System;
            class C
            {
                void M()
                {
                    int p = 0;
                    ref readonly int refP = ref p!;
                    Console.WriteLine(p);
                }
            }
            """);
 
    [Fact]
    public Task TestScopedReadonlyRefLocalWithNoReassignment1()
        => TestAsync(
            """
            using System;
            class C
            {
                void M1()
                {
                    int p = 0;
                    scoped ref readonly int refP = ref p!;
                    Console.WriteLine(p);
                }
            }
            """);
 
    [Fact]
    public Task TestPointerCausingPossibleReassignment()
        => TestAsync(
            """
            using System;
            class C
            {
                unsafe void M()
                {
                    int [|p|] = 0;
                    int* pointer = &[|p|];
                    Console.WriteLine([|p|]);
                }
            }
            """);
 
    [Fact]
    public Task TestRefExtensionMethodCausingPossibleReassignment()
        => TestAsync(
            """
            using System;
            static class C
            {
                void M()
                {
                    int [|p|] = 0;
                    [|p|].M2();
                    Console.WriteLine([|p|]);
                }
 
                static void M2(this ref int p) { }
            }
            """);
 
    [Fact]
    public Task TestMutatingStructMethod()
        => TestAsync(
            """
            using System;
            struct S
            {
                int f;
 
                void M(S p)
                {
                    p.MutatingMethod();
                    Console.WriteLine(p);
                }
 
                void MutatingMethod() => this = default;
            }
            """);
 
    [Fact]
    public Task TestReassignmentWhenDeclaredWithDeconstruction()
        => TestAsync(
            """
            using System;
            class C
            {
                void M()
                {
                    var ([|x|], y) = Goo();
                    [|x|] = 0;
                    Console.WriteLine([|x|]);
                }
 
                (int x, int y) Goo() => default;
            }
            """);
 
    [Fact]
    public Task TestReassignmentThroughDeconstruction()
        => TestAsync(
            """
            using System;
            class C
            {
                void M()
                {
                    var [|x|] = 0;
                    ([|x|], _) = Goo();
                    Console.WriteLine([|x|]);
                }
 
                (int x, int y) Goo() => default;
            }
            """);
 
    [Fact]
    public Task TestTopLevelNotReassigned()
        => TestAsync(
            """
            int p;
            p = 0;
            Console.WriteLine(p);
            """);
 
    [Fact]
    public Task TestTopLevelReassigned()
        => TestAsync(
            """
            int [|p|] = 1;
            [|p|] = 0;
            Console.WriteLine([|p|]);
            """);
 
    [Fact]
    public Task TestTopLevelArgsParameterNotReassigned()
        => TestAsync(
            """
            Console.WriteLine(args);
            """);
 
    [Fact]
    public Task TestTopLevelArgsParameterReassigned()
        => TestAsync(
            """
 
            [|args|] = null
            Console.WriteLine([|args|]);
 
            """);
 
    [Fact]
    public Task TestUsedInThisBase1()
        => TestAsync(
            """
            class C
            {
                public C(int [|x|])
                    : this([|x|]++, true)
                {
                }
 
                public C(int x, bool b)
                {
                }
            }
            """);
 
    [Fact]
    public Task TestUsedInThisBase2()
        => TestAsync(
            """
            class C
            {
                public C(string s)
                    : this(int.TryParse(s, out var [|x|]) ? [|x|]++ : 0, true)
                {
                }
 
                public C(int x, bool b)
                {
                }
            }
            """);
 
    [Fact]
    public Task TestRecord1()
        => TestAsync(
            """
            record X(int [|x|]) : Y([|x|]++)
            {
            }
 
            record Y(int x)
            {
            }
            """);
 
    [Fact]
    public Task TestRecord2()
        => TestAsync(
            """
            record X(int [|x|])
            {
                int Y = [|x|]++;
            }
            """);
 
    [Fact]
    public Task TestRecord3()
        => TestAsync(
            """
            record struct X(int [|x|])
            {
                int Y = [|x|]++;
            }
            """);
 
    [Fact]
    public Task TestClass1()
        => TestAsync(
            """
            class X(int [|x|]) : Y([|x|]++)
            {
            }
 
            class Y(int x)
            {
            }
            """);
 
    [Fact]
    public Task TestClass2()
        => TestAsync(
            """
            class X(int [|x|])
            {
                int Y = [|x|]++;
            }
            """);
 
    [Fact]
    public Task TestClass3()
        => TestAsync(
            """
            class X(int [|x|])
            {
                int Y() => [|x|]++;
            }
            """);
 
    [Fact]
    public Task TestStruct2()
        => TestAsync(
            """
            struct X(int [|x|])
            {
                int Y = [|x|]++;
            }
            """);
 
    [Fact]
    public Task TestStruct3()
        => TestAsync(
            """
            struct X(int [|x|])
            {
                int Y() => [|x|]++;
            }
            """);
 
    [Fact]
    public Task TestExceptionVariableReassignment()
        => TestAsync(
            """
            using System;
            class C
            {
                void M()
                {
                    try { }
                    catch (Exception ex)
                    {
                        [|ex|] = null;
                    }
                }
            }
            """);
 
    [Fact]
    public Task TestLocalReassignedInExceptionFilter()
        => TestAsync(
            """
            using System;
            class C
            {
                void M()
                {
                    try { }
                    catch (Exception ex) when (([|ex|] = null) == null) { }
                }
            }
            """);
 
    [Fact]
    public Task TestLocalReassignedInCaseGuard()
        => TestAsync(
            """
            using System;
            class C
            {
                void M()
                {
                    switch (1)
                    {
                        case var [|x|] when [|x|]++ == 2: break;
                    }
                }
            }
            """);
 
    [Fact]
    public Task TestLocalWithMultipleDeclarators()
        => TestAsync(
            """
            using System;
            class C
            {
                void M()
                {
                    int a, [|b|] = 1, c;
                    [|b|] = 2;
                    Console.WriteLine([|b|]);
                }
            }
            """);
 
    [Fact]
    public Task TestForLoop()
        => TestAsync(
            """
            using System;
            class C
            {
                void M()
                {
                    for (int [|i|] = 0; [|i|] < 10; [|i|]++)
                        Console.WriteLine([|i|]);
                }
            }
            """);
 
    [Fact]
    public Task TestForeach()
        => TestAsync(
            """
            using System;
            class C
            {
                void M(string[] args)
                {
                    foreach (var arg in args)
                        Console.WriteLine(arg);
                }
            }
            """);
 
    [Fact]
    public Task TestWriteThroughOneBranch()
        => TestAsync(
            """
            using System;
            class C
            {
                void M()
                {
                    int p;
                    if (p)
                        p = 1;
 
                    p = 0;
                    Console.WriteLine(p);
                }
            }
            """);
 
    [Fact]
    public Task TestDuplicateMethod()
        => TestAsync(
            """
            class C
            {
                void M(int [|p|])
                {
                    [|p|] = 1;
                }
 
                void M(int [|p|])
                {
                    [|p|] = 1;
                }
            }
            """);
 
    [Fact]
    public Task TestDuplicateParameter()
        => TestAsync(
            """
            class C
            {
                void M(int p, int p)
                {
                    p = 1;
                }
            }
            """);
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/58161")]
    public Task TestRefToSuppression1()
        => TestAsync(
            """
            #nullable enable
 
            using System.Diagnostics.CodeAnalysis;
            using System.Threading;
 
            class C
            {
                public static T EnsureInitialized<T>([NotNull] ref T? [|target|]) where T : class
                    => Volatile.Read(ref [|target|]!);
            }
            """);
 
    [Fact]
    public Task TestPrimaryConstructor1()
        => TestAsync(
            """
            class C(int [|p|])
            {
                void M()
                {
                    [|p|] = 1;
                }
            }
            """);
 
    [Fact]
    public Task TestPrimaryConstructor2()
        => TestAsync(
            """
            class C(int p)
            {
                void M()
                {
                    var v = new C(p: 1);
                }
            }
            """);
 
    [Fact]
    public Task TestPrimaryConstructor3()
        => TestAsync(
            """
            partial class C(int [|p|])
            {
            }
 
            partial class C
            {
                void M()
                {
                    [|p|] = 1;
                }
            }
            """);
 
    [Fact]
    public Task TestPrimaryConstructor4()
        => TestAsync(
            """
            class B(int p)
            {
            }
 
            partial class C(int [|p|]) : B([|p|] = 1)
            {
            }
            """);
 
    [Fact]
    public Task TestPrimaryConstructor5()
        => TestAsync(
            """
            <Workspace>
                <Project Language="C#" AssemblyName="Assembly1" CommonReferences="true">
                    <Document>
            partial class C(int [|p|])
            {
            }
                    </Document>
                    <Document>
            partial class C
            {
                void M()
                {
                    [|p|] = 1;
                }
            }
                    </Document>
                </Project>
            </Workspace>
            """);
}