File: FlowAnalysis\PatternsVsRegions.cs
Web Access
Project: src\src\Compilers\CSharp\Test\Emit3\Microsoft.CodeAnalysis.CSharp.Emit3.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.Emit3.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.
 
#nullable disable
 
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Test.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.CSharp.UnitTests
{
    /// <summary>
    /// Region analysis tests involving pattern-matching constructs.
    /// </summary>
    public partial class PatternsVsRegions : FlowTestBase
    {
        [Fact]
        public void RegionInIsPattern01()
        {
            var dataFlowAnalysisResults = CompileAndAnalyzeDataFlowExpression(
@"class Program
{
    public static void Main(string[] args)
    {
        object o = args;
        if (/*<bind>*/o is int i && i > 10/*</bind>*/) {}
    }
}");
            Assert.Equal("i", GetSymbolNamesJoined(dataFlowAnalysisResults.VariablesDeclared));
            Assert.Equal("o", GetSymbolNamesJoined(dataFlowAnalysisResults.DataFlowsIn));
            Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.DataFlowsOut));
            Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.AlwaysAssigned));
            Assert.Equal("o, i", GetSymbolNamesJoined(dataFlowAnalysisResults.ReadInside));
            Assert.Equal("i", GetSymbolNamesJoined(dataFlowAnalysisResults.WrittenInside));
            Assert.Equal("args", GetSymbolNamesJoined(dataFlowAnalysisResults.ReadOutside));
            Assert.Equal("args, o", GetSymbolNamesJoined(dataFlowAnalysisResults.WrittenOutside));
            Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.Captured));
            Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.CapturedInside));
            Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.CapturedOutside));
            Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.UnsafeAddressTaken));
        }
 
        [Fact]
        public void RegionInIsPattern02()
        {
            var dataFlowAnalysisResults = CompileAndAnalyzeDataFlowExpression(
@"class Program
{
    public static void Main(string[] args)
    {
        object o = args;
        if (/*<bind>*/o is int i/*</bind>*/ && i > 10) {}
    }
}");
            Assert.Equal("i", GetSymbolNamesJoined(dataFlowAnalysisResults.VariablesDeclared));
            Assert.Equal("o", GetSymbolNamesJoined(dataFlowAnalysisResults.DataFlowsIn));
            Assert.Equal("i", GetSymbolNamesJoined(dataFlowAnalysisResults.DataFlowsOut));
            Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.AlwaysAssigned));
            Assert.Equal("o", GetSymbolNamesJoined(dataFlowAnalysisResults.ReadInside));
            Assert.Equal("i", GetSymbolNamesJoined(dataFlowAnalysisResults.WrittenInside));
            Assert.Equal("args, i", GetSymbolNamesJoined(dataFlowAnalysisResults.ReadOutside));
            Assert.Equal("args, o", GetSymbolNamesJoined(dataFlowAnalysisResults.WrittenOutside));
            Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.Captured));
            Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.CapturedInside));
            Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.CapturedOutside));
            Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.UnsafeAddressTaken));
        }
 
        [Fact]
        public void RegionInIsPattern03()
        {
            var dataFlowAnalysisResults = CompileAndAnalyzeDataFlowExpression(
@"class Program
{
    public static void Main(string[] args)
    {
        object o = args;
        if (o is int i && /*<bind>*/i > 10/*</bind>*/) {}
    }
}");
            Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.VariablesDeclared));
            Assert.Equal("i", GetSymbolNamesJoined(dataFlowAnalysisResults.DataFlowsIn));
            Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.DataFlowsOut));
            Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.AlwaysAssigned));
            Assert.Equal("i", GetSymbolNamesJoined(dataFlowAnalysisResults.ReadInside));
            Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.WrittenInside));
            Assert.Equal("args, o", GetSymbolNamesJoined(dataFlowAnalysisResults.ReadOutside));
            Assert.Equal("args, o, i", GetSymbolNamesJoined(dataFlowAnalysisResults.WrittenOutside));
            Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.Captured));
            Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.CapturedInside));
            Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.CapturedOutside));
            Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.UnsafeAddressTaken));
        }
 
        [Fact]
        public void RegionInIsPattern04()
        {
            var dataFlowAnalysisResults = CompileAndAnalyzeDataFlowExpression(
@"class Program
{
    public static void Main(string[] args)
    {
        int o = args.Length;
        if (/*<bind>*/o is int i/*</bind>*/ && i > 10) {}
    }
}");
            Assert.Equal("i", GetSymbolNamesJoined(dataFlowAnalysisResults.VariablesDeclared));
            Assert.Equal("o", GetSymbolNamesJoined(dataFlowAnalysisResults.DataFlowsIn));
            Assert.Equal("i", GetSymbolNamesJoined(dataFlowAnalysisResults.DataFlowsOut));
            Assert.Equal("i", GetSymbolNamesJoined(dataFlowAnalysisResults.AlwaysAssigned));
            Assert.Equal("o", GetSymbolNamesJoined(dataFlowAnalysisResults.ReadInside));
            Assert.Equal("i", GetSymbolNamesJoined(dataFlowAnalysisResults.WrittenInside));
            Assert.Equal("args, i", GetSymbolNamesJoined(dataFlowAnalysisResults.ReadOutside));
            Assert.Equal("args, o", GetSymbolNamesJoined(dataFlowAnalysisResults.WrittenOutside));
            Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.Captured));
            Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.CapturedInside));
            Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.CapturedOutside));
            Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.UnsafeAddressTaken));
        }
 
        [Fact]
        public void RegionInIsPattern05()
        {
            var dataFlowAnalysisResults = CompileAndAnalyzeDataFlowExpression(
@"class Program
{
    public static void Main(string[] args)
    {
        if (/*<bind>*/args is [] i/*</bind>*/) {}
    }
}");
            Assert.Equal("i", GetSymbolNamesJoined(dataFlowAnalysisResults.VariablesDeclared));
            Assert.Equal("args", GetSymbolNamesJoined(dataFlowAnalysisResults.DataFlowsIn));
            Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.DataFlowsOut));
            Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.AlwaysAssigned));
            Assert.Equal("args", GetSymbolNamesJoined(dataFlowAnalysisResults.ReadInside));
            Assert.Equal("i", GetSymbolNamesJoined(dataFlowAnalysisResults.WrittenInside));
            Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.ReadOutside));
            Assert.Equal("args", GetSymbolNamesJoined(dataFlowAnalysisResults.WrittenOutside));
            Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.Captured));
            Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.CapturedInside));
            Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.CapturedOutside));
            Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.UnsafeAddressTaken));
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79489")]
        public void RegionInIsPattern06()
        {
            var dataFlowAnalysisResults = CompileAndAnalyzeDataFlowExpression("""
                C.Use(doLogic: true);
 
                class C
                {
                    public static (bool, string? errorMessage) M() => (false, "Something went wrong");
 
                    public static void Use(bool doLogic)
                    {
                        if (doLogic)
                        {
                            if (/*<bind>*/M() is (false, var errorMessage)/*</bind>*/)
                            {
                                Console.Error.WriteLine(errorMessage);
                            }
                        }
                    }
                }
                """);
            VerifyDataFlowAnalysis("""
                VariablesDeclared: errorMessage
                AlwaysAssigned: 
                Captured: 
                CapturedInside: 
                CapturedOutside: 
                DataFlowsIn: 
                DataFlowsOut: errorMessage
                DefinitelyAssignedOnEntry: doLogic
                DefinitelyAssignedOnExit: doLogic
                ReadInside: 
                ReadOutside: doLogic, errorMessage
                WrittenInside: errorMessage
                WrittenOutside: doLogic
                """, dataFlowAnalysisResults);
        }
 
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79489")]
        public void RegionInSwitch01()
        {
            var comp = CreateCompilation("""
                static void PatternMatchingUsage()
                {
                  var t = (1, 2);
                  // selection start
                  switch (t)
                  {
                    case (var x, 2):
                      Console.WriteLine(x);
                      break;
                  }
                  // selection end
                }
                """);
 
            var tree = comp.SyntaxTrees[0];
            var model = comp.GetSemanticModel(tree);
 
            var @switch = tree.GetRoot().DescendantNodes().OfType<SwitchStatementSyntax>().Single();
            var dataFlowAnalysisResults = model.AnalyzeDataFlow(@switch);
            VerifyDataFlowAnalysis("""
                VariablesDeclared: x
                AlwaysAssigned: 
                Captured: 
                CapturedInside: 
                CapturedOutside: 
                DataFlowsIn: t
                DataFlowsOut: 
                DefinitelyAssignedOnEntry: t, args
                DefinitelyAssignedOnExit: t, args
                ReadInside: t, x
                ReadOutside: 
                WrittenInside: x
                WrittenOutside: t, args
                """, dataFlowAnalysisResults);
        }
 
        [Fact]
        public void RegionInIsPattern07()
        {
            var dataFlowAnalysisResults = CompileAndAnalyzeDataFlowExpression("""
                C.Use(doLogic: true);
 
                class C
                {
                    public static (bool, (string result, string? errorMessage)) M() => (false, "Something went wrong");
 
                    public static void Use(bool doLogic)
                    {
                        if (doLogic)
                        {
                            if (/*<bind>*/M() is (false, (var inner, var errorMessage))/*</bind>*/)
                            {
                                Console.Error.WriteLine(errorMessage);
                            }
                        }
                    }
                }
 
                """);
            VerifyDataFlowAnalysis("""
                VariablesDeclared: inner, errorMessage
                AlwaysAssigned: 
                Captured: 
                CapturedInside: 
                CapturedOutside: 
                DataFlowsIn: 
                DataFlowsOut: errorMessage
                DefinitelyAssignedOnEntry: doLogic
                DefinitelyAssignedOnExit: doLogic
                ReadInside: 
                ReadOutside: doLogic, errorMessage
                WrittenInside: inner, errorMessage
                WrittenOutside: doLogic
                """, dataFlowAnalysisResults);
        }
 
        [Fact]
        public void RegionInIsPattern08()
        {
            var dataFlowAnalysisResults = CompileAndAnalyzeDataFlowExpression("""
                C.Use(doLogic: true);
 
                class C
                {
                    public static (string result, (bool, string? errorMessage)) M() => (false, "Something went wrong");
 
                    public static void Use(bool doLogic)
                    {
                        if (doLogic)
                        {
                            if (/*<bind>*/M() is (var outer, (true, var errorMessage) tuple)/*</bind>*/)
                            {
                                Console.Error.WriteLine(errorMessage);
                            }
                        }
                    }
                }
 
                """);
            VerifyDataFlowAnalysis("""
                VariablesDeclared: outer, errorMessage, tuple
                AlwaysAssigned: 
                Captured: 
                CapturedInside: 
                CapturedOutside: 
                DataFlowsIn: 
                DataFlowsOut: errorMessage
                DefinitelyAssignedOnEntry: doLogic
                DefinitelyAssignedOnExit: doLogic
                ReadInside: 
                ReadOutside: doLogic, errorMessage
                WrittenInside: outer, errorMessage, tuple
                WrittenOutside: doLogic
                """, dataFlowAnalysisResults);
        }
 
        [Fact]
        public void RegionInIsPattern09()
        {
            var dataFlowAnalysisResults = CompileAndAnalyzeDataFlowExpression("""
                C.Use(doLogic: true);
 
                class C
                {
                    public static (string result, (bool, string? errorMessage)) M() => (false, "Something went wrong");
 
                    public static void Use(bool doLogic)
                    {
                        if (doLogic)
                        {
                            if (/*<bind>*/M() is { result: var outer, Item2: { errorMessage: var errorMessage } tuple })/*</bind>*/)
                            {
                                Console.Error.WriteLine(errorMessage);
                            }
                        }
                    }
                }
 
                """);
            VerifyDataFlowAnalysis("""
                VariablesDeclared: outer, errorMessage, tuple
                AlwaysAssigned: outer, errorMessage, tuple
                Captured: 
                CapturedInside: 
                CapturedOutside: 
                DataFlowsIn: 
                DataFlowsOut: errorMessage
                DefinitelyAssignedOnEntry: doLogic
                DefinitelyAssignedOnExit: doLogic, outer, errorMessage, tuple
                ReadInside: 
                ReadOutside: doLogic, errorMessage
                WrittenInside: outer, errorMessage, tuple
                WrittenOutside: doLogic
                """, dataFlowAnalysisResults);
        }
 
        [Fact]
        public void RegionWithinSubpattern01()
        {
            var comp = CreateCompilation("""
                int x = 1;
                if (x is var y and 2) { }
                """);
 
            comp.VerifyDiagnostics();
 
            var tree = comp.SyntaxTrees[0];
            var model = comp.GetSemanticModel(tree);
            var constantPattern = tree.GetRoot().DescendantNodes().OfType<ConstantPatternSyntax>().Single();
            Assert.Equal("2", constantPattern.ToString());
 
            Assert.Throws<ArgumentException>(() => model.AnalyzeDataFlow(constantPattern));
            var dataFlow = model.AnalyzeDataFlow(constantPattern.Expression);
            VerifyDataFlowAnalysis("""
                VariablesDeclared: 
                AlwaysAssigned: 
                Captured: 
                CapturedInside: 
                CapturedOutside: 
                DataFlowsIn: 
                DataFlowsOut: 
                DefinitelyAssignedOnEntry: x, y, args
                DefinitelyAssignedOnExit: x, y, args
                ReadInside: 
                ReadOutside: x
                WrittenInside: 
                WrittenOutside: x, y, args
                """,
                dataFlow);
        }
 
        [Fact]
        public void RegionWithinSubpattern02()
        {
            var comp = CreateCompilation("""
                static class Program
                {
                    static void Main()
                    {
                        int x = 1;
                        if (x is var y and x.P and > 2) { }
                    }
 
                    extension (ref int i)
                    {
                        public int P => i;
                    }
                }
                """);
 
            comp.VerifyDiagnostics(
                // (6,28): error CS9135: A constant value of type 'int' is expected
                //         if (x is var y and x.P and > 2) { }
                Diagnostic(ErrorCode.ERR_ConstantValueOfTypeExpected, "x.P").WithArguments("int").WithLocation(6, 28));
 
            var tree = comp.SyntaxTrees[0];
            var model = comp.GetSemanticModel(tree);
            var constantPattern = tree.GetRoot().DescendantNodes().OfType<ConstantPatternSyntax>().Single();
            Assert.Equal("x.P", constantPattern.ToString());
 
            Assert.Throws<ArgumentException>(() => model.AnalyzeDataFlow(constantPattern));
            var dataFlow = model.AnalyzeDataFlow(constantPattern.Expression);
            VerifyDataFlowAnalysis("""
                VariablesDeclared: 
                AlwaysAssigned: 
                Captured: 
                CapturedInside: 
                CapturedOutside: 
                DataFlowsIn: x
                DataFlowsOut: 
                DefinitelyAssignedOnEntry: x, y
                DefinitelyAssignedOnExit: x, y
                ReadInside: x
                ReadOutside: x
                WrittenInside: 
                WrittenOutside: x, y
                """,
                dataFlow);
        }
 
        [Fact]
        public void RegionWithinSubpattern03()
        {
            var comp = CreateCompilation("""
                bool x = false;
                if (x is var y and (x is var z)) { }
                """);
 
            comp.VerifyDiagnostics(
                // (2,21): error CS9135: A constant value of type 'bool' is expected
                // if (x is var y and (x is var z)) { }
                Diagnostic(ErrorCode.ERR_ConstantValueOfTypeExpected, "x is var z").WithArguments("bool").WithLocation(2, 21));
 
            var tree = comp.SyntaxTrees[0];
            var model = comp.GetSemanticModel(tree);
            var constantPattern = tree.GetRoot().DescendantNodes().OfType<ConstantPatternSyntax>().Single();
            Assert.Equal("(x is var z)", constantPattern.ToString());
 
            Assert.Throws<ArgumentException>(() => model.AnalyzeDataFlow(constantPattern));
            var dataFlow = model.AnalyzeDataFlow(constantPattern.Expression);
            VerifyDataFlowAnalysis("""
                VariablesDeclared: z
                AlwaysAssigned: z
                Captured: 
                CapturedInside: 
                CapturedOutside: 
                DataFlowsIn: x
                DataFlowsOut: 
                DefinitelyAssignedOnEntry: x, y, args
                DefinitelyAssignedOnExit: x, y, z, args
                ReadInside: x
                ReadOutside: x
                WrittenInside: z
                WrittenOutside: x, y, args
                """,
                dataFlow);
        }
 
        [Fact]
        public void RegionITuplePattern()
        {
            var source = """
                class Program
                {
                    static bool M(object obj)
                    {
                        return obj is (object x, string y, int z);
                    }
                }
                """;
            var compilation = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp);
            compilation.VerifyEmitDiagnostics();
 
            var tree = compilation.SyntaxTrees[0];
            var model = compilation.GetSemanticModel(tree);
            var node = tree.GetRoot().DescendantNodes().OfType<IsPatternExpressionSyntax>().Single();
            Assert.Equal("obj is (object x, string y, int z)", node.ToString());
            VerifyDataFlowAnalysis("""
                VariablesDeclared: x, y, z
                AlwaysAssigned: 
                Captured: 
                CapturedInside: 
                CapturedOutside: 
                DataFlowsIn: obj
                DataFlowsOut: 
                DefinitelyAssignedOnEntry: obj
                DefinitelyAssignedOnExit: obj
                ReadInside: obj
                ReadOutside: 
                WrittenInside: x, y, z
                WrittenOutside: obj
                """,
                model.AnalyzeDataFlow(node));
        }
 
        [Fact]
        public void RegionListPattern()
        {
            var source = """
                class Program
                {
                    static bool M(object[] arr)
                    {
                        return arr is [object x, string y, int z] arr1;
                    }
                }
                """;
            var compilation = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp);
            compilation.VerifyEmitDiagnostics();
 
            var tree = compilation.SyntaxTrees[0];
            var model = compilation.GetSemanticModel(tree);
            var node = tree.GetRoot().DescendantNodes().OfType<IsPatternExpressionSyntax>().Single();
            Assert.Equal("arr is [object x, string y, int z] arr1", node.ToString());
            VerifyDataFlowAnalysis("""
                VariablesDeclared: x, y, z, arr1
                AlwaysAssigned: 
                Captured: 
                CapturedInside: 
                CapturedOutside: 
                DataFlowsIn: arr
                DataFlowsOut: 
                DefinitelyAssignedOnEntry: arr
                DefinitelyAssignedOnExit: arr
                ReadInside: arr
                ReadOutside: 
                WrittenInside: x, y, z, arr1
                WrittenOutside: arr
                """,
                model.AnalyzeDataFlow(node));
        }
 
        [Fact]
        public void RegionSlicePattern()
        {
            var source = """
                class Program
                {
                    static bool M(object[] arr)
                    {
                        return arr is [object x, ..object[] y];
                    }
                }
                """;
            var compilation = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp);
            compilation.VerifyEmitDiagnostics();
 
            var tree = compilation.SyntaxTrees[0];
            var model = compilation.GetSemanticModel(tree);
            var node = tree.GetRoot().DescendantNodes().OfType<IsPatternExpressionSyntax>().Single();
            Assert.Equal("arr is [object x, ..object[] y]", node.ToString());
            VerifyDataFlowAnalysis("""
                VariablesDeclared: x, y
                AlwaysAssigned: 
                Captured: 
                CapturedInside: 
                CapturedOutside: 
                DataFlowsIn: arr
                DataFlowsOut: 
                DefinitelyAssignedOnEntry: arr
                DefinitelyAssignedOnExit: arr
                ReadInside: arr
                ReadOutside: 
                WrittenInside: x, y
                WrittenOutside: arr
                """,
                model.AnalyzeDataFlow(node));
        }
 
        [Fact]
        public void RegionNotPattern()
        {
            var source = """
                class Program
                {
                    static void M(object obj)
                    {
                        if (obj is not string s)
                        {
                        }
                    }
                }
                """;
            var compilation = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp);
            compilation.VerifyEmitDiagnostics();
 
            var tree = compilation.SyntaxTrees[0];
            var model = compilation.GetSemanticModel(tree);
            var node = tree.GetRoot().DescendantNodes().OfType<IsPatternExpressionSyntax>().Single();
            Assert.Equal("obj is not string s", node.ToString());
            VerifyDataFlowAnalysis("""
                VariablesDeclared: s
                AlwaysAssigned: 
                Captured: 
                CapturedInside: 
                CapturedOutside: 
                DataFlowsIn: obj
                DataFlowsOut: 
                DefinitelyAssignedOnEntry: obj
                DefinitelyAssignedOnExit: obj
                ReadInside: obj
                ReadOutside: 
                WrittenInside: s
                WrittenOutside: obj
                """,
                model.AnalyzeDataFlow(node));
        }
 
        [Fact]
        public void RegionOrPattern()
        {
            var source = """
                class Program
                {
                    static void M(object obj)
                    {
                        if (obj is string s or int i)
                        {
                        }
                    }
                }
                """;
            var compilation = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp);
            compilation.VerifyEmitDiagnostics(
                // (5,27): error CS8780: A variable may not be declared within a 'not' or 'or' pattern.
                //         if (obj is string s or int i)
                Diagnostic(ErrorCode.ERR_DesignatorBeneathPatternCombinator, "s").WithLocation(5, 27),
                // (5,36): error CS8780: A variable may not be declared within a 'not' or 'or' pattern.
                //         if (obj is string s or int i)
                Diagnostic(ErrorCode.ERR_DesignatorBeneathPatternCombinator, "i").WithLocation(5, 36));
 
            var tree = compilation.SyntaxTrees[0];
            var model = compilation.GetSemanticModel(tree);
            var node = tree.GetRoot().DescendantNodes().OfType<IsPatternExpressionSyntax>().Single();
            Assert.Equal("obj is string s or int i", node.ToString());
            VerifyDataFlowAnalysis("""
                VariablesDeclared: s, i
                AlwaysAssigned: 
                Captured: 
                CapturedInside: 
                CapturedOutside: 
                DataFlowsIn: obj
                DataFlowsOut: 
                DefinitelyAssignedOnEntry: obj
                DefinitelyAssignedOnExit: obj
                ReadInside: obj
                ReadOutside: 
                WrittenInside: 
                WrittenOutside: obj
                """,
                model.AnalyzeDataFlow(node));
        }
 
        [Fact]
        public void RegionSplitState()
        {
            var source = """
                class Program
                {
                    static void M(object obj)
                    {
                        if (obj is var s)
                        {
                            throw null!;
                        }
 
                        return;
                    }
                }
                """;
            var compilation = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp);
            compilation.VerifyEmitDiagnostics();
 
            var tree = compilation.SyntaxTrees[0];
            var model = compilation.GetSemanticModel(tree);
            var node = tree.GetRoot().DescendantNodes().OfType<IsPatternExpressionSyntax>().Single();
            Assert.Equal("obj is var s", node.ToString());
            VerifyDataFlowAnalysis("""
                VariablesDeclared: s
                AlwaysAssigned: s
                Captured: 
                CapturedInside: 
                CapturedOutside: 
                DataFlowsIn: obj
                DataFlowsOut: 
                DefinitelyAssignedOnEntry: obj
                DefinitelyAssignedOnExit: obj, s
                ReadInside: obj
                ReadOutside: 
                WrittenInside: s
                WrittenOutside: obj
                """,
                model.AnalyzeDataFlow(node));
        }
    }
}