File: ValueTracking\CSharpValueTrackingTests.cs
Web Access
Project: src\src\EditorFeatures\Test\Microsoft.CodeAnalysis.EditorFeatures.UnitTests.csproj (Microsoft.CodeAnalysis.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.Linq;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Remote.Testing;
using Microsoft.CodeAnalysis.Test.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.Editor.UnitTests.ValueTracking;
 
[UseExportProvider]
public class CSharpValueTrackingTests : AbstractBaseValueTrackingTests
{
    protected override TestWorkspace CreateWorkspace(string code, TestComposition composition)
        => TestWorkspace.CreateCSharp(code, composition: composition);
 
    [Theory, CombinatorialData]
    public async Task TestProperty(TestHost testHost)
    {
        var code =
            """
 
            class C
            {
                public string $$S { get; set; } = "";
 
                public void SetS(string s)
                {
                    S = s;
                }
 
                public string GetS() => S;
            }
 
            """;
        using var workspace = CreateWorkspace(code, testHost);
 
        //
        // property S 
        //  |> S = s [Code.cs:7]
        //  |> public string S { get; set; } [Code.cs:3]
        //
        await ValidateItemsAsync(
            workspace,
            itemInfo: new[]
            {
                (7, "s"),
                (3, "public string S { get; set; } = \"\";"),
            });
    }
 
    [Theory, CombinatorialData]
    public async Task TestPropertyWithThis(TestHost testHost)
    {
        var code =
            """
 
            class C
            {
                public string $$S { get; set; } = "";
 
                public void SetS(string s)
                {
                    this.S = s;
                }
 
                public string GetS() => this.S;
            }
 
            """;
        using var workspace = CreateWorkspace(code, testHost);
 
        //
        // property S 
        //  |> S = s [Code.cs:7]
        //  |> public string S { get; set; } [Code.cs:3]
        //
        await ValidateItemsAsync(
            workspace,
            itemInfo: new[]
            {
                (7, "s"),
                (3, "public string S { get; set; } = \"\";"),
            });
    }
 
    [Theory, CombinatorialData]
    public async Task TestField(TestHost testHost)
    {
        var code =
            """
 
            class C
            {
                private string $$_s = "";
 
                public void SetS(string s)
                {
                    _s = s;
                }
 
                public string GetS() => _s;
            }
            """;
        using var workspace = CreateWorkspace(code, testHost);
        var initialItems = await GetTrackedItemsAsync(workspace);
 
        //
        // field _s 
        //  |> _s = s [Code.cs:7]
        //  |> string _s = "" [Code.cs:3]
        //
        await ValidateItemsAsync(
            workspace,
            itemInfo: new[]
            {
                (7, "s"),
                (3, """
                _s = ""
                """)
            });
    }
 
    [Theory, CombinatorialData]
    public async Task TestFieldWithThis(TestHost testHost)
    {
        var code =
            """
 
            class C
            {
                private string $$_s = "";
 
                public void SetS(string s)
                {
                    this._s = s;
                }
 
                public string GetS() => this._s;
            }
            """;
        using var workspace = CreateWorkspace(code, testHost);
        var initialItems = await GetTrackedItemsAsync(workspace);
 
        //
        // field _s 
        //  |> this._s = s [Code.cs:7]
        //  |> string _s = "" [Code.cs:3]
        //
        await ValidateItemsAsync(
            workspace,
            itemInfo: new[]
            {
                (7, "s"),
                (3, """
                _s = ""
                """)
            });
    }
 
    [Theory, CombinatorialData]
    public async Task TestLocal(TestHost testHost)
    {
        var code =
            """
 
            class C
            {
                public int Add(int x, int y)
                {
                    var $$z = x;
                    z += y;
                    return z;
                }
            }
            """;
        using var workspace = CreateWorkspace(code, testHost);
        var initialItems = await GetTrackedItemsAsync(workspace);
 
        //
        // local variable z
        //  |> z += y [Code.cs:6]
        //  |> var z = x [Code.cs:5]
        //
        Assert.Equal(2, initialItems.Length);
        ValidateItem(initialItems[0], 6);
        ValidateItem(initialItems[1], 5);
    }
 
    [Theory, CombinatorialData]
    public async Task TestParameter(TestHost testHost)
    {
        var code =
            """
 
            class C
            {
                public int Add(int $$x, int y)
                {
                    x += y;
                    return x;
                }
            }
            """;
        using var workspace = CreateWorkspace(code, testHost);
        var initialItems = await GetTrackedItemsAsync(workspace);
 
        //
        // parameter x 
        //  |> x += y [Code.cs:5]
        //  |> Add(int x, int y) [Code.cs:3]
        //
        Assert.Equal(2, initialItems.Length);
        ValidateItem(initialItems[0], 5);
        ValidateItem(initialItems[1], 3);
    }
 
    [Theory, CombinatorialData]
    public async Task TestParameter2(TestHost testHost)
    {
        var code =
            """
 
            class C
            {
                public void InvokeM(string arg)
                {
                    M(arg);
                    M();
                    M("test");
                }
 
                public void M(string? $$x = null)
                {
                }
            }
            """;
        using var workspace = CreateWorkspace(code, testHost);
        var initialItems = await GetTrackedItemsAsync(workspace);
 
        //
        // public void M(|string? x = null|)
        //  M(|"test"|)
        //  |M("test")|
        //  M(|arg|)
        //  |M(arg)|
        // 
        Assert.Equal(1, initialItems.Length);
        ValidateItem(initialItems[0], 10);
 
        var items = await ValidateChildrenAsync(
            workspace,
            initialItems.Single(),
            childInfo: new[]
            {
                (7, """
                "test"
                """),           // M(|"test"|)
                (7, """
                M("test")
                """),           // |M("test")|
                (5, "arg"),     // M(|arg|)
                (5, "M(arg)"),  // |M(arg)|
            });
    }
 
    [Theory, CombinatorialData]
    public async Task TestMissingOnMethod(TestHost testHost)
    {
        var code =
            """
 
            class C
            {
                public int $$Add(int x, int y)
                {
                    x += y;
                    return x;
                }
            }
            """;
        using var workspace = CreateWorkspace(code, testHost);
        var initialItems = await GetTrackedItemsAsync(workspace);
        Assert.Empty(initialItems);
    }
 
    [Theory, CombinatorialData]
    public async Task TestMissingOnClass(TestHost testHost)
    {
        var code =
            """
 
            class $$C
            {
                public int Add(int x, int y)
                {
                    x += y;
                    return x;
                }
            }
            """;
        using var workspace = CreateWorkspace(code, testHost);
        var initialItems = await GetTrackedItemsAsync(workspace);
        Assert.Empty(initialItems);
    }
 
    [Theory, CombinatorialData]
    public async Task TestMissingOnNamespace(TestHost testHost)
    {
        var code =
            """
 
            namespace $$N
            {
                class C
                {
                    public int Add(int x, int y)
                    {
                        x += y;
                        return x;
                    }
                }
            }
            """;
        using var workspace = CreateWorkspace(code, testHost);
        var initialItems = await GetTrackedItemsAsync(workspace);
        Assert.Empty(initialItems);
    }
 
    [Theory, CombinatorialData]
    public async Task MethodTracking1(TestHost testHost)
    {
        var code =
            """
 
            class C
            {
                public string S { get; set; } = "";
 
                public void SetS(string s)
                {
                    S$$ = s;
                }
 
                public string GetS() => S;
            }
 
            class Other
            {
                public void CallS(C c, string str)
                {
                    c.SetS(str);
                }
 
                public void CallS(C c)
                {
                    CallS(c, CalculateDefault(c));
                }
 
                private string CalculateDefault(C c)
                {
                    if (c is null)
                    {
                        return "null";
                    }
 
                    if (string.IsNullOrEmpty(c.S))
                    {
                        return "defaultstring";
                    }
 
                    return "";
                }
            }
 
            """;
        using var workspace = CreateWorkspace(code, testHost);
        var initialItems = await GetTrackedItemsAsync(workspace);
 
        //
        // S = s; [Code.cs:7]
        //  |> S = [|s|] [Code.cs:7]
        //    |> [|c.SetS(str)|]; [Code.cs:17]
        //    |> c.SetS([|str|]); [Code.cs:17]
        //      |> CallS([|c|], CalculateDefault(c)) [Code.cs:22]
        //      |> CallS(c, [|CalculateDefault(c)|]) [Code.cs:22]
        //         |>  return "" [Code.cs:37]
        //         |>  return "defaultstring" [Code.cs:34]
        //         |> return "null" [Code.cs:29]
        // 
        Assert.Equal(1, initialItems.Length);
        ValidateItem(initialItems[0], 7);
 
        var items = await ValidateChildrenAsync(
            workspace,
            initialItems.Single(),
            childInfo: new[]
            {
                (17, "str"), // |> c.SetS([|str|]); [Code.cs:17]
                (17, "c.SetS(str)"), // |> [|c.SetS(str)|]; [Code.cs:17]
            });
 
        // |> [|c.SetS(s)|]; [Code.cs:17]
        await ValidateChildrenEmptyAsync(workspace, items[1]);
 
        // |> c.SetS([|s|]); [Code.cs:17]
        items = await ValidateChildrenAsync(
            workspace,
            items[0],
            childInfo: new[]
            {
                (22, "c" ), // |> CallS([|c|], CalculateDefault(c)) [Code.cs:22]
                (22, "c" ), // |> CallS(c, CalculateDefault([|c|])) [Code.cs:22]
                (22, "CalculateDefault(c)" ), // |> CallS(c, [|CalculateDefault(c)|]) [Code.cs:22]
                (22, "CallS(c, CalculateDefault(c))" ) // |> [|CallS(c, CalculateDefault(c))|] [Code.cs:22]
            });
 
        // |> CallS([|c|], CalculateDefault(c)) [Code.cs:22]
        await ValidateChildrenEmptyAsync(workspace, items[0]);
        // |> CallS(c, CalculateDefault([|c|])) [Code.cs:22]
        await ValidateChildrenEmptyAsync(workspace, items[1]);
        // |> CallS(c, [|CalculateDefault(c)|]) [Code.cs:22]
        await ValidateChildrenEmptyAsync(workspace, items[3]);
 
        // |> CallS(c, [|CalculateDefault(c)|]) [Code.cs:22]
        var children = await ValidateChildrenAsync(
            workspace,
            items[2],
            childInfo: new[]
            {
                (37, """
                ""
                """), // |>  return "" [Code.cs:37]
                (34, """
                "defaultstring"
                """), // |>  return "defaultstring" [Code.cs:34]
                (29, """
                "null"
                """), // |> return "null" [Code.cs:29]
            });
 
        foreach (var child in children)
        {
            await ValidateChildrenEmptyAsync(workspace, child);
        }
    }
 
    [Theory, CombinatorialData]
    public async Task MethodTracking2(TestHost testHost)
    {
        var code =
            """
 
            class C
            {
                public string S { get; set; } = "";
 
                public void SetS(string s)
                {
                    S$$ = s;
                }
 
                public string GetS() => S;
            }
 
            class Other
            {
                private readonly string _adornment;
                public Other(string adornment)
                {
                    _adornment = adornment;
                }
 
                public void CallS(C c, string s)
                {
                    c.SetS(s);
                }
 
                public void CallS(C c)
                {
                    CallS(c, CalculateDefault(c) + _adornment);
                }
 
                private string CalculateDefault(C c)
                {
                    if (c is null)
                    {
                        return "null";
                    }
 
                    if (string.IsNullOrEmpty(c.S))
                    {
                        return "defaultstring";
                    }
 
                    return "";
                }
            }
 
            class Program
            {
                public static void Main(string[] args)
                {
                    var other = new Other("some value");
                    var c = new C();
                    other.CallS(c);
                }
            }
 
            """;
        using var workspace = CreateWorkspace(code, testHost);
        var initialItems = await GetTrackedItemsAsync(workspace);
 
        //
        // S = s; [Code.cs:7]
        //  |> S = [|s|] [Code.cs:7]
        //    |> [|c.SetS(s)|]; [Code.cs:23]
        //    |> c.SetS([|s|]); [Code.cs:23]
        //      |> CallS([|c|], CalculateDefault(c) + _adornment) [Code.cs:28]
        //        |> other.CallS([|c|]); [Code.cs:53]
        //      |> CallS(c, CalculateDefault(c) + [|_adornment|]) [Code.cs:28]
        //        |> _adornment = [|adornment|]; [Code.cs:18]
        //          |> var other = new Other([|"some value"|]); [Code.cs:51]
        //      |> CallS(c, CalculateDefault([|c|]) + _adornment) [Code.cs:28]
        //        |> other.CallS([|c|]); [Code.cs:53]
        //      |> CallS(c, [|CalculateDefault(c)|] + _adornment) [Code.cs:28]
        //        |>  return "" [Code.cs:37]
        //        |>  return "defaultstring" [Code.cs:34]
        //        |> return "null" [Code.cs:29]
        //      |> [|CallS(c, CalculateDefault(c) + _adornment)|] [Code.cs:28]
        //
        Assert.Equal(1, initialItems.Length);
        ValidateItem(initialItems[0], 7);
 
        var items = await ValidateChildrenAsync(
            workspace,
            initialItems.Single(),
            childInfo: new[]
            {
                (23, "s"), // |> c.SetS([|s|]); [Code.cs:23]
                (23, "c.SetS(s)"), // |> c.SetS(s); [Code.cs:23]
            });
 
        // |> c.SetS(s); [Code.cs:23]
        await ValidateChildrenEmptyAsync(workspace, items[1]);
 
        // |> c.SetS([|s|]); [Code.cs:23]
        items = await ValidateChildrenAsync(
            workspace,
            items[0],
            childInfo: new[]
            {
                (28, "c" ), // |> CallS([|c|], CalculateDefault(c) + _adornment) [Code.cs:28]
                (28, "_adornment" ), // |> CallS(c, CalculateDefault(c) + [|_adornment|]) [Code.cs:28]
                (28, "c" ), // |> CallS(c, CalculateDefault([|c|]) + _adornment) [Code.cs:28]
                (28, "CalculateDefault(c)" ), // |> CallS(c, [|CalculateDefault|](c) + _adornment) [Code.cs:28]
                (28, "CallS(c, CalculateDefault(c) + _adornment)" ), // |> [|CallS(c, CalculateDefault(c) + _adornment)|] [Code.cs:28]
            });
 
        // |> CallS([|c|], CalculateDefault(c) + _adornment) [Code.cs:28]
        var children = await ValidateChildrenAsync(
            workspace,
            items[0],
            childInfo: new[]
            {
                (53, "other.CallS(c)"), // |> other.CallS([|c|]); [Code.cs:53]
            });
 
        await ValidateChildrenEmptyAsync(workspace, children);
 
        // |> CallS(c, CalculateDefault([|c|]) + _adornment) [Code.cs:28]
        children = await ValidateChildrenAsync(
            workspace,
            items[2],
            childInfo: new[]
            {
                (53, "other.CallS(c)"), // |> other.CallS([|c|]); [Code.cs:53]
            });
 
        await ValidateChildrenEmptyAsync(workspace, children);
 
        // |> CallS(c, CalculateDefault(c) + [|_adornment|]) [Code.cs:28]
        children = await ValidateChildrenAsync(
            workspace,
            items[1],
            childInfo: new[]
            {
                (18, "adornment"), // |> _adornment = [|adornment|] [Code.cs:18]
            });
 
        children = await ValidateChildrenAsync(
            workspace,
            children.Single(),
            childInfo: new[]
            {
                (51, """
                "some value"
                """) // |> var other = new Other([|"some value"|]); [Code.cs:51]
            });
        await ValidateChildrenEmptyAsync(workspace, children);
 
        // |> CallS(c, [|CalculateDefault(c)|] + _adornment) [Code.cs:28]
        children = await ValidateChildrenAsync(
            workspace,
            items[3],
            childInfo: new[]
            {
                (43, """
                ""
                """), // |>  return "" [Code.cs:37]
                (40, """
                "defaultstring"
                """), // |>  return "defaultstring" [Code.cs:34]
                (35, """
                "null"
                """), // |> return "null" [Code.cs:29]
            });
 
        await ValidateChildrenEmptyAsync(workspace, children);
 
        // |> [|CallS(c, CalculateDefault(c) + _adornment)|] [Code.cs:28]
        await ValidateChildrenEmptyAsync(workspace, items[4]);
    }
 
    [Theory, CombinatorialData]
    public async Task MethodTracking3(TestHost testHost)
    {
        var code =
            """
 
            using System.Threading.Tasks;
 
            namespace N
            {
                class C
                {
                    public int Add(int x, int y)
                    {
                        x += y;
                        return x;
                    }
 
                    public Task<int> AddAsync(int x, int y) => Task.FromResult(Add(x,y));
 
                    public async Task<int> Double(int x)
                    {
                        x = await AddAsync(x, x);
                        return $$x;
                    }
                }
            }
            """;
        //
        //  |> return [|x|] [Code.cs:18]
        //    |> x = await AddAsync([|x|], x) [Code.cs:17]
        //    |> x = await AddAsync(x, [|x|]) [Code.cs:17]
        //    |> x = await [|AddAsync(x, x)|] [Code.cs:17]
        //      |> [|Task.FromResult|](Add(x, y)) [Code.cs:13]
        //      |> Task.FromResult([|Add(x, y)|]) [Code.cs:13]
        //        |> return x [Code.cs:11]
        using var workspace = CreateWorkspace(code, testHost);
        var initialItems = await GetTrackedItemsAsync(workspace);
        Assert.Equal(1, initialItems.Length);
        ValidateItem(initialItems.Single(), 18, "x"); // |> return [|x|] [Code.cs:18]
 
        var children = await ValidateChildrenAsync(
            workspace,
            initialItems.Single(),
            childInfo: new[]
            {
                (17, "x"), // |> x = await AddAsync([|x|], x) [Code.cs:17]
                (17, "x"), // |> x = await AddAsync(x, [|x|]) [Code.cs:17]
                (17, "AddAsync(x, x)") // |> x = await [|AddAsync(x, x)|] [Code.cs:17]
            });
 
        // |> x = await [|AddAsync(x, x)|] [Code.cs:17]
        children = await ValidateChildrenAsync(
            workspace,
            children[2],
            childInfo: new[]
            {
                (13, "x"), // |> Task.FromResult(Add([|x|], y)) [Code.cs:13]
                (13, "y"), // |> Task.FromResult(Add(x, [|y|])) [Code.cs:13]
                (13, "Add(x,y)"),  // |> Task.FromResult([|Add(x, y)|]) [Code.cs:13]
                (13, "Task.FromResult(Add(x,y))"), // |> [|Task.FromResult|](Add(x, y)) [Code.cs:13]
            });
 
        // |> [|Task.FromResult|](Add(x, y)) [Code.cs:13]
        await ValidateChildrenEmptyAsync(workspace, children[3]);
 
        // |> Task.FromResult([|Add(x, y)|]) [Code.cs:13]
        children = await ValidateChildrenAsync(
            workspace,
            children[2],
            childInfo: new[]
            {
                (10, "x") // |> return x [Code.cs:10]
            });
    }
 
    [Theory, CombinatorialData]
    public async Task OutParam(TestHost testHost)
    {
        var code = """
 
            class C
            {
                bool TryConvertInt(object o, out int i)
                {
                    if (int.TryParse(o.ToString(), out i))
                    {
                        return true;
                    }
 
                    return false;
                }
 
                void M()
                {
                    int i = 0;
                    object o = "5";
 
                    if (TryConvertInt(o, out i))
                    {
                        Console.WriteLine($$i);
                    }
                    else
                    {
                        i = 2;
                    }
                }
            }
            """;
 
        //
        //  |> Console.WriteLine($$i); [Code.cs:20]
        //    |> i = [|2|] [Code.cs:24]
        //    |> if (TryConvertInt(o, out [|i|])) [Code.cs:18]
        //      |> if (int.TryParse(o.ToString(), out [|i|])) [Code.cs:5]
        //    |> int i = 0 [Code.cs:15]
        using var workspace = CreateWorkspace(code, testHost);
        var initialItems = await GetTrackedItemsAsync(workspace);
 
        Assert.Equal(1, initialItems.Length);
 
        // |> Console.WriteLine($$i);[Code.cs:20]
        ValidateItem(initialItems.Single(), 20, "i");
 
        var children = await ValidateChildrenAsync(
            workspace,
            initialItems.Single(),
            childInfo: new[]
            {
                (24, "2"), // |> i = [|2|] [Code.cs:24]
                (18, "i"), // |> if (TryConvertInt(o, out [|i|])) [Code.cs:18]
                (15, "0"), // |> int i = 0 [Code.cs:15]
            });
 
        // |> i = [|2|] [Code.cs:24]
        await ValidateChildrenEmptyAsync(workspace, children[0]);
 
        // |> if (TryConvertInt(o, out [|i|])) [Code.cs:18]
        children = await ValidateChildrenAsync(
            workspace,
            children[1],
            childInfo: new[]
            {
                (5, "i") // |> if (int.TryParse(o.ToString(), out [|i|])) [Code.cs:5]
            });
 
        await ValidateChildrenEmptyAsync(workspace, children.Single());
    }
 
    [Theory, CombinatorialData]
    public async Task TestVariableReferenceStart(TestHost testHost)
    {
        var code =
            """
 
            class Test
            {
                public static void M()
                {
                    int x = GetM();
                    Console.Write(x);
                    var y = $$x + 1;
                }
 
                public static int GetM()
                {
                    var x = 0;
                    return x;
                }
            }
            """;
 
        //
        //  |> var y = x + 1; [Code.cs:7]
        //    |> int x = GetM() [Code.cs:5]
        //      |> return x; [Code.cs:13]
        //        |> var x = 0; [Code.cs:12]
        using var workspace = CreateWorkspace(code, testHost);
 
        var items = await ValidateItemsAsync(
            workspace,
            itemInfo: new[]
            {
                (7, "x") // |> var y = [|x|] + 1; [Code.cs:7]
            });
 
        items = await ValidateChildrenAsync(
            workspace,
            items.Single(),
            childInfo: new[]
            {
                (5, "GetM()") // |> int x = [|GetM()|] [Code.cs:5]
            });
 
        items = await ValidateChildrenAsync(
            workspace,
            items.Single(),
            childInfo: new[]
            {
                (13, "x") // |> return [|x|]; [Code.cs:13]
            });
 
        items = await ValidateChildrenAsync(
            workspace,
            items.Single(),
            childInfo: new[]
            {
                (12, "0") // |> var x = [|0|]; [Code.cs:12]
            });
 
        await ValidateChildrenEmptyAsync(workspace, items.Single());
    }
 
    [Theory, CombinatorialData]
    public async Task TestVariableReferenceStart2(TestHost testHost)
    {
        var code =
            """
 
            class Test
            {
                public static void M()
                {
                    int x = GetM();
                    Console.Write($$x);
                    var y = x + 1;
                }
 
                public static int GetM()
                {
                    var x = 0;
                    return x;
                }
            }
            """;
 
        //
        //  |> Console.Write(x); [Code.cs:6]
        //    |> int x = GetM() [Code.cs:5]
        //      |> return x; [Code.cs:13]
        //        |> var x = 0; [Code.cs:12]
        using var workspace = CreateWorkspace(code, testHost);
 
        var items = await ValidateItemsAsync(
            workspace,
            itemInfo: new[]
            {
                (6, "x") // |> Console.Write([|x|]); [Code.cs:7]
            });
 
        items = await ValidateChildrenAsync(
            workspace,
            items.Single(),
            childInfo: new[]
            {
                (5, "GetM()") // |> int x = [|GetM()|] [Code.cs:5]
            });
 
        items = await ValidateChildrenAsync(
            workspace,
            items.Single(),
            childInfo: new[]
            {
                (13, "x") // |> return [|x|]; [Code.cs:13]
            });
 
        items = await ValidateChildrenAsync(
            workspace,
            items.Single(),
            childInfo: new[]
            {
                (12, "0") // |> var x = [|0|]; [Code.cs:12]
            });
 
        await ValidateChildrenEmptyAsync(workspace, items.Single());
    }
 
    [Theory, CombinatorialData]
    public async Task TestVariableReferenceStart3(TestHost testHost)
    {
        var code =
            """
 
            class Test
            {
                public static void M()
                {
                    int x = GetM();
                    Console.Write($$x);
                    var y = x + 1;
                    x += 1;
                    Console.Write(x);
                    Console.Write(y);
                }
 
                public static int GetM()
                {
                    var x = 0;
                    return x;
                }
            }
            """;
 
        //
        //  |> Console.Write(x); [Code.cs:6]
        //    |> int x = GetM() [Code.cs:5]
        //      |> return x; [Code.cs:13]
        //        |> var x = 0; [Code.cs:12]
        //    |> x += 1 [Code.cs:8]
        using var workspace = CreateWorkspace(code, testHost);
 
        var items = await ValidateItemsAsync(
            workspace,
            itemInfo: new[]
            {
                (6, "x") // |> Console.Write([|x|]); [Code.cs:7]
            });
 
        items = await ValidateChildrenAsync(
            workspace,
            items.Single(),
            childInfo: new[]
            {
                (8, "1"),      // |> x += 1; [Codec.s:8]
                (5, "GetM()"), // |> int x = [|GetM()|] [Code.cs:5]
            });
 
        await ValidateChildrenEmptyAsync(workspace, items[0]);
 
        items = await ValidateChildrenAsync(
            workspace,
            items[1],
            childInfo: new[]
            {
                (16, "x") // |> return [|x|]; [Code.cs:13]
            });
 
        items = await ValidateChildrenAsync(
            workspace,
            items.Single(),
            childInfo: new[]
            {
                (15, "0") // |> var x = [|0|]; [Code.cs:12]
            });
 
        await ValidateChildrenEmptyAsync(workspace, items.Single());
    }
 
    [Theory, CombinatorialData]
    public async Task TestMultipleDeclarators(TestHost testHost)
    {
        var code =
            """
 
            class Test
            {
                public static void M()
                {
                    int x = GetM(), z = 5;
                    Console.Write($$x);
                    var y = x + 1 + z;
                    x += 1;
                    Console.Write(x);
                    Console.Write(y);
                }
 
                public static int GetM()
                {
                    var x = 0;
                    return x;
                }
            }
            """;
 
        //
        //  |> Console.Write(x); [Code.cs:6]
        //    |> int x = GetM() [Code.cs:5]
        //      |> return x; [Code.cs:13]
        //        |> var x = 0; [Code.cs:12]
        //    |> x += 1 [Code.cs:8]
        using var workspace = CreateWorkspace(code, testHost);
 
        var items = await ValidateItemsAsync(
            workspace,
            itemInfo: new[]
            {
                (6, "x") // |> Console.Write([|x|]); [Code.cs:7]
            });
 
        items = await ValidateChildrenAsync(
            workspace,
            items.Single(),
            childInfo: new[]
            {
                (8, "1"),      // |> x += 1; [Codec.s:8]
                (5, "GetM()"), // |> int x = [|GetM()|] [Code.cs:5]
            });
 
        await ValidateChildrenEmptyAsync(workspace, items[0]);
 
        items = await ValidateChildrenAsync(
            workspace,
            items[1],
            childInfo: new[]
            {
                (16, "x") // |> return [|x|]; [Code.cs:13]
            });
 
        items = await ValidateChildrenAsync(
            workspace,
            items.Single(),
            childInfo: new[]
            {
                (15, "0") // |> var x = [|0|]; [Code.cs:12]
            });
 
        await ValidateChildrenEmptyAsync(workspace, items.Single());
    }
 
    [Theory, CombinatorialData]
    public async Task TestIndex(TestHost testHost)
    {
        var code =
            """
 
            class Test
            {
                public int this[string $$key] => 0;
 
                public int M(Test localTest)
                {
                    var assignedVariable = this["test"];
                    System.Console.WriteLine(this["test"]);
                    
                    return localTest["test"];
                }
            }
            """;
 
        //
        //  |> public int this[string [|key|]] => 0; [Code.cs:4]
        //    |> return [|localTest|][[|"test"|]]; [Code.cs:10]
        //    |> System.Console.WriteLine(this[[|"test"|]]); [Code.cs:8]
        //    |> var [|assignedVariable = this["test"]|]; [Code.cs:7]
        using var workspace = CreateWorkspace(code, testHost);
 
        var items = await ValidateItemsAsync(
            workspace,
            itemInfo: new[]
            {
                (3, "string key") // |>public int this[[|string key|]] => 0; [Code.cs:4]
            });
 
        items = await ValidateChildrenAsync(
            workspace,
            items.Single(),
            childInfo: new[]
            {
                (10, "localTest"), // return [|localTest|]["test"]; [Code.cs:10] (This is included because it is part of a return statement, and follows same logic as other references for if it is tracked)
                (10, """
                "test"
                """),  // return localTest[[|"test"|]]; [Code.cs:10]
                (8, """
                "test"
                """),   // System.Console.WriteLine(this[[|"test"|]]); [Code.cs:8]
                (7, """
                "test"
                """),   // var assignedVariable = this[[|"test"|]]; [Code.cs:7]
            });
 
        await ValidateChildrenEmptyAsync(workspace, items[0]);
        await ValidateChildrenEmptyAsync(workspace, items[1]);
        await ValidateChildrenEmptyAsync(workspace, items[2]);
        await ValidateChildrenEmptyAsync(workspace, items[3]);
    }
 
    [Theory, CombinatorialData]
    public async Task TestPropertyValue(TestHost testHost)
    {
        var code =
            """
 
            class Test
            {
                private int _i;
                public int I 
                {
                    get => _i;
                    set 
                    {
                        _i = $$value;
                    }
                }
 
                public int M(Test localTest)
                {
                    localTest.I = 5;
                }
            }
            """;
        //  _i = [|value|]; [Code.cs:9]
        //    |> localTest.I = [|5|]; [Code.cs:15]
        using var workspace = CreateWorkspace(code, testHost);
 
        var items = await ValidateItemsAsync(
            workspace,
            itemInfo: new[]
            {
                (9, "value") // _i = [|value|]; [Code.cs:9]
            });
 
        items = await ValidateChildrenAsync(
            workspace,
            items.Single(),
            childInfo: new[]
            {
                (15, "5") // localTest.I = [|5|]; [Code.cs:15]
            });
 
        await ValidateChildrenEmptyAsync(workspace, items.Single());
    }
}