File: IOperation\IOperationTests_IAnonymousFunctionExpression.cs
Web Access
Project: src\src\Compilers\CSharp\Test\IOperation\Microsoft.CodeAnalysis.CSharp.IOperation.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.IOperation.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.Linq;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.FlowAnalysis;
using Microsoft.CodeAnalysis.Operations;
using Microsoft.CodeAnalysis.Test.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.CSharp.UnitTests
{
    public class IOperationTests_IAnonymousFunctionExpression : SemanticModelTestBase
    {
        [CompilerTrait(CompilerFeature.IOperation)]
        [Fact]
        public void IAnonymousFunctionExpression_BoundLambda_HasValidLambdaExpressionTree()
        {
            string source = @"
using System;
 
class Program
{
    static void Main(string[] args)
    {
        /*<bind>*/Action x = () => F();/*</bind>*/
    }
 
    static void F()
    {
    }
}
";
            string expectedOperationTree = @"
IVariableDeclarationGroupOperation (1 declarations) (OperationKind.VariableDeclarationGroup, Type: null) (Syntax: 'Action x = () => F();')
  IVariableDeclarationOperation (1 declarators) (OperationKind.VariableDeclaration, Type: null) (Syntax: 'Action x = () => F()')
    Declarators:
        IVariableDeclaratorOperation (Symbol: System.Action x) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'x = () => F()')
          Initializer: 
            IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= () => F()')
              IDelegateCreationOperation (OperationKind.DelegateCreation, Type: System.Action, IsImplicit) (Syntax: '() => F()')
                Target: 
                  IAnonymousFunctionOperation (Symbol: lambda expression) (OperationKind.AnonymousFunction, Type: null) (Syntax: '() => F()')
                    IBlockOperation (2 statements) (OperationKind.Block, Type: null, IsImplicit) (Syntax: 'F()')
                      IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsImplicit) (Syntax: 'F()')
                        Expression: 
                          IInvocationOperation (void Program.F()) (OperationKind.Invocation, Type: System.Void) (Syntax: 'F()')
                            Instance Receiver: 
                              null
                            Arguments(0)
                      IReturnOperation (OperationKind.Return, Type: null, IsImplicit) (Syntax: 'F()')
                        ReturnedValue: 
                          null
    Initializer: 
      null
";
            var expectedDiagnostics = DiagnosticDescription.None;
 
            VerifyOperationTreeAndDiagnosticsForTest<LocalDeclarationStatementSyntax>(source, expectedOperationTree, expectedDiagnostics);
        }
 
        [CompilerTrait(CompilerFeature.IOperation)]
        [Fact]
        public void IAnonymousFunctionExpression_BoundLambda_HasValidLambdaExpressionTree_JustBindingLambdaReturnsOnlyIAnonymousFunctionExpression()
        {
            string source = @"
using System;
 
class Program
{
    static void Main(string[] args)
    {
        Action x = /*<bind>*/() => F()/*</bind>*/;
    }
 
    static void F()
    {
    }
}
";
            string expectedOperationTree = @"
IAnonymousFunctionOperation (Symbol: lambda expression) (OperationKind.AnonymousFunction, Type: null) (Syntax: '() => F()')
  IBlockOperation (2 statements) (OperationKind.Block, Type: null, IsImplicit) (Syntax: 'F()')
    IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsImplicit) (Syntax: 'F()')
      Expression: 
        IInvocationOperation (void Program.F()) (OperationKind.Invocation, Type: System.Void) (Syntax: 'F()')
          Instance Receiver: 
            null
          Arguments(0)
    IReturnOperation (OperationKind.Return, Type: null, IsImplicit) (Syntax: 'F()')
      ReturnedValue: 
        null
";
            var expectedDiagnostics = DiagnosticDescription.None;
 
            VerifyOperationTreeAndDiagnosticsForTest<ParenthesizedLambdaExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics);
        }
 
        [CompilerTrait(CompilerFeature.IOperation)]
        [Fact]
        public void IAnonymousFunctionExpression_BoundLambda_HasValidLambdaExpressionTree_ExplicitCast()
        {
            string source = @"
using System;
 
class Program
{
    static void Main(string[] args)
    {
        /*<bind>*/Action x = (Action)(() => F());/*</bind>*/
    }
 
    static void F()
    {
    }
}
";
            string expectedOperationTree = @"
IVariableDeclarationGroupOperation (1 declarations) (OperationKind.VariableDeclarationGroup, Type: null) (Syntax: 'Action x =  ... () => F());')
  IVariableDeclarationOperation (1 declarators) (OperationKind.VariableDeclaration, Type: null) (Syntax: 'Action x =  ... (() => F())')
    Declarators:
        IVariableDeclaratorOperation (Symbol: System.Action x) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'x = (Action)(() => F())')
          Initializer: 
            IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= (Action)(() => F())')
              IDelegateCreationOperation (OperationKind.DelegateCreation, Type: System.Action) (Syntax: '(Action)(() => F())')
                Target: 
                  IAnonymousFunctionOperation (Symbol: lambda expression) (OperationKind.AnonymousFunction, Type: null) (Syntax: '() => F()')
                    IBlockOperation (2 statements) (OperationKind.Block, Type: null, IsImplicit) (Syntax: 'F()')
                      IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsImplicit) (Syntax: 'F()')
                        Expression: 
                          IInvocationOperation (void Program.F()) (OperationKind.Invocation, Type: System.Void) (Syntax: 'F()')
                            Instance Receiver: 
                              null
                            Arguments(0)
                      IReturnOperation (OperationKind.Return, Type: null, IsImplicit) (Syntax: 'F()')
                        ReturnedValue: 
                          null
    Initializer: 
      null
";
            var expectedDiagnostics = DiagnosticDescription.None;
 
            VerifyOperationTreeAndDiagnosticsForTest<LocalDeclarationStatementSyntax>(source, expectedOperationTree, expectedDiagnostics);
        }
 
        [CompilerTrait(CompilerFeature.IOperation)]
        [Fact]
        public void IAnonymousFunctionExpression_BoundLambda_HasValidLambdaExpressionTree2()
        {
            string source = @"
using System;
 
class Program
{
    static void Main(string[] args)
    {
        Action x = /*<bind>*/() => F()/*</bind>*/;
    }
 
    static void F()
    {
    }
}
";
            string expectedOperationTree = @"
IAnonymousFunctionOperation (Symbol: lambda expression) (OperationKind.AnonymousFunction, Type: null) (Syntax: '() => F()')
  IBlockOperation (2 statements) (OperationKind.Block, Type: null, IsImplicit) (Syntax: 'F()')
    IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsImplicit) (Syntax: 'F()')
      Expression: 
        IInvocationOperation (void Program.F()) (OperationKind.Invocation, Type: System.Void) (Syntax: 'F()')
          Instance Receiver: 
            null
          Arguments(0)
    IReturnOperation (OperationKind.Return, Type: null, IsImplicit) (Syntax: 'F()')
      ReturnedValue: 
        null
";
            var expectedDiagnostics = DiagnosticDescription.None;
 
            VerifyOperationTreeAndDiagnosticsForTest<ParenthesizedLambdaExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics);
        }
 
        [CompilerTrait(CompilerFeature.IOperation)]
        [Fact]
        public void IAnonymousFunctionExpression_UnboundLambdaAsVar_HasValidLambdaExpressionTree()
        {
            string source = @"
using System;
 
class Program
{
    static void Main(string[] args)
    {
        /*<bind>*/var x = () => F();/*</bind>*/
    }
 
    static void F()
    {
    }
}
";
            string expectedOperationTree = @"
    IVariableDeclarationGroupOperation (1 declarations) (OperationKind.VariableDeclarationGroup, Type: null) (Syntax: 'var x = () => F();')
  IVariableDeclarationOperation (1 declarators) (OperationKind.VariableDeclaration, Type: null) (Syntax: 'var x = () => F()')
    Declarators:
        IVariableDeclaratorOperation (Symbol: System.Action x) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'x = () => F()')
          Initializer:
            IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= () => F()')
              IDelegateCreationOperation (OperationKind.DelegateCreation, Type: System.Action, IsImplicit) (Syntax: '() => F()')
                Target:
                  IAnonymousFunctionOperation (Symbol: lambda expression) (OperationKind.AnonymousFunction, Type: null) (Syntax: '() => F()')
                    IBlockOperation (2 statements) (OperationKind.Block, Type: null, IsImplicit) (Syntax: 'F()')
                      IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsImplicit) (Syntax: 'F()')
                        Expression:
                          IInvocationOperation (void Program.F()) (OperationKind.Invocation, Type: System.Void) (Syntax: 'F()')
                            Instance Receiver:
                              null
                            Arguments(0)
                      IReturnOperation (OperationKind.Return, Type: null, IsImplicit) (Syntax: 'F()')
                        ReturnedValue:
                          null
    Initializer:
      null
";
            var expectedDiagnostics = DiagnosticDescription.None;
 
            VerifyOperationTreeAndDiagnosticsForTest<LocalDeclarationStatementSyntax>(source, expectedOperationTree, expectedDiagnostics);
        }
 
        [CompilerTrait(CompilerFeature.IOperation)]
        [Fact]
        public void IAnonymousFunctionExpression_UnboundLambdaAsDelegate_HasValidLambdaExpressionTree()
        {
            string source = @"
using System;
 
class Program
{
    static void Main(string[] args)
    {
        /*<bind>*/Action<int> x = () => F();/*</bind>*/
    }
 
    static void F()
    {
    }
}
";
            string expectedOperationTree = @"
IVariableDeclarationGroupOperation (1 declarations) (OperationKind.VariableDeclarationGroup, Type: null, IsInvalid) (Syntax: 'Action<int> ...  () => F();')
  IVariableDeclarationOperation (1 declarators) (OperationKind.VariableDeclaration, Type: null, IsInvalid) (Syntax: 'Action<int> ... = () => F()')
    Declarators:
        IVariableDeclaratorOperation (Symbol: System.Action<System.Int32> x) (OperationKind.VariableDeclarator, Type: null, IsInvalid) (Syntax: 'x = () => F()')
          Initializer:
            IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null, IsInvalid) (Syntax: '= () => F()')
              IDelegateCreationOperation (OperationKind.DelegateCreation, Type: System.Action<System.Int32>, IsInvalid, IsImplicit) (Syntax: '() => F()')
                Target:
                  IAnonymousFunctionOperation (Symbol: lambda expression) (OperationKind.AnonymousFunction, Type: null, IsInvalid) (Syntax: '() => F()')
                    IBlockOperation (1 statements) (OperationKind.Block, Type: null, IsImplicit) (Syntax: 'F()')
                      IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsImplicit) (Syntax: 'F()')
                        Expression:
                          IInvocationOperation (void Program.F()) (OperationKind.Invocation, Type: System.Void) (Syntax: 'F()')
                            Instance Receiver:
                              null
                            Arguments(0)
    Initializer: 
      null
";
            var expectedDiagnostics = new DiagnosticDescription[] {
                // (8,38): error CS1593: Delegate 'Action<int>' does not take 0 arguments
                //         /*<bind>*/Action<int> x = () => F();/*</bind>*/
                Diagnostic(ErrorCode.ERR_BadDelArgCount, "=>").WithArguments("System.Action<int>", "0").WithLocation(8, 38)
            };
 
            VerifyOperationTreeAndDiagnosticsForTest<LocalDeclarationStatementSyntax>(source, expectedOperationTree, expectedDiagnostics);
        }
 
        [CompilerTrait(CompilerFeature.IOperation)]
        [Fact]
        public void IAnonymousFunctionExpression_UnboundLambdaAsDelegate_HasValidLambdaExpressionTree_ExplicitValidCast()
        {
            string source = @"
using System;
 
class Program
{
    static void Main(string[] args)
    {
        /*<bind>*/Action<int> x = (Action)(() => F());/*</bind>*/
    }
 
    static void F()
    {
    }
}
";
            string expectedOperationTree = @"
IVariableDeclarationGroupOperation (1 declarations) (OperationKind.VariableDeclarationGroup, Type: null, IsInvalid) (Syntax: 'Action<int> ... () => F());')
  IVariableDeclarationOperation (1 declarators) (OperationKind.VariableDeclaration, Type: null, IsInvalid) (Syntax: 'Action<int> ... (() => F())')
    Declarators:
        IVariableDeclaratorOperation (Symbol: System.Action<System.Int32> x) (OperationKind.VariableDeclarator, Type: null, IsInvalid) (Syntax: 'x = (Action)(() => F())')
          Initializer: 
            IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null, IsInvalid) (Syntax: '= (Action)(() => F())')
              IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Action<System.Int32>, IsInvalid, IsImplicit) (Syntax: '(Action)(() => F())')
                Conversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
                Operand: 
                  IDelegateCreationOperation (OperationKind.DelegateCreation, Type: System.Action, IsInvalid) (Syntax: '(Action)(() => F())')
                    Target: 
                      IAnonymousFunctionOperation (Symbol: lambda expression) (OperationKind.AnonymousFunction, Type: null, IsInvalid) (Syntax: '() => F()')
                        IBlockOperation (2 statements) (OperationKind.Block, Type: null, IsInvalid, IsImplicit) (Syntax: 'F()')
                          IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsInvalid, IsImplicit) (Syntax: 'F()')
                            Expression: 
                              IInvocationOperation (void Program.F()) (OperationKind.Invocation, Type: System.Void, IsInvalid) (Syntax: 'F()')
                                Instance Receiver: 
                                  null
                                Arguments(0)
                          IReturnOperation (OperationKind.Return, Type: null, IsInvalid, IsImplicit) (Syntax: 'F()')
                            ReturnedValue: 
                              null
    Initializer: 
      null
";
            var expectedDiagnostics = new DiagnosticDescription[] {
                // CS0029: Cannot implicitly convert type 'System.Action' to 'System.Action<int>'
                //         /*<bind>*/Action<int> x = (Action)(() => F());/*</bind>*/
                Diagnostic(ErrorCode.ERR_NoImplicitConv, "(Action)(() => F())").WithArguments("System.Action", "System.Action<int>").WithLocation(8, 35)
            };
 
            VerifyOperationTreeAndDiagnosticsForTest<LocalDeclarationStatementSyntax>(source, expectedOperationTree, expectedDiagnostics);
        }
 
        [CompilerTrait(CompilerFeature.IOperation)]
        [Fact]
        public void IAnonymousFunctionExpression_UnboundLambdaAsDelegate_HasValidLambdaExpressionTree_ExplicitInvalidCast()
        {
            string source = @"
using System;
 
class Program
{
    static void Main(string[] args)
    {
        /*<bind>*/Action<int> x = (Action<int>)(() => F());/*</bind>*/
    }
 
    static void F()
    {
    }
}
";
            string expectedOperationTree = @"
IVariableDeclarationGroupOperation (1 declarations) (OperationKind.VariableDeclarationGroup, Type: null, IsInvalid) (Syntax: 'Action<int> ... () => F());')
  IVariableDeclarationOperation (1 declarators) (OperationKind.VariableDeclaration, Type: null, IsInvalid) (Syntax: 'Action<int> ... (() => F())')
    Declarators:
        IVariableDeclaratorOperation (Symbol: System.Action<System.Int32> x) (OperationKind.VariableDeclarator, Type: null, IsInvalid) (Syntax: 'x = (Action ... (() => F())')
          Initializer:
            IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null, IsInvalid) (Syntax: '= (Action<i ... (() => F())')
              IDelegateCreationOperation (OperationKind.DelegateCreation, Type: System.Action<System.Int32>, IsInvalid) (Syntax: '(Action<int>)(() => F())')
                Target:
                  IAnonymousFunctionOperation (Symbol: lambda expression) (OperationKind.AnonymousFunction, Type: null, IsInvalid) (Syntax: '() => F()')
                    IBlockOperation (1 statements) (OperationKind.Block, Type: null, IsImplicit) (Syntax: 'F()')
                      IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsImplicit) (Syntax: 'F()')
                        Expression:
                          IInvocationOperation (void Program.F()) (OperationKind.Invocation, Type: System.Void) (Syntax: 'F()')
                            Instance Receiver:
                              null
                            Arguments(0)
    Initializer: 
      null
";
            var expectedDiagnostics = new DiagnosticDescription[] {
                // (8,52): error CS1593: Delegate 'Action<int>' does not take 0 arguments
                //         /*<bind>*/Action<int> x = (Action<int>)(() => F());/*</bind>*/
                Diagnostic(ErrorCode.ERR_BadDelArgCount, "=>").WithArguments("System.Action<int>", "0").WithLocation(8, 52)
            };
 
            VerifyOperationTreeAndDiagnosticsForTest<LocalDeclarationStatementSyntax>(source, expectedOperationTree, expectedDiagnostics);
        }
 
        [CompilerTrait(CompilerFeature.IOperation)]
        [Fact]
        public void IAnonymousFunctionExpression_UnboundLambda_ReferenceEquality()
        {
            string source = @"
using System;
 
class Program
{
    static void Main(string[] args)
    {
        /*<bind>*/var x = () => F();/*</bind>*/
    }
 
    static void F()
    {
    }
}
";
 
            var compilation = CreateCompilationWithMscorlib40AndSystemCore(source);
            var syntaxTree = compilation.SyntaxTrees[0];
            var semanticModel = compilation.GetSemanticModel(syntaxTree);
 
            var variableDeclaration = syntaxTree.GetRoot().DescendantNodes().OfType<LocalDeclarationStatementSyntax>().Single();
            var lambdaSyntax = (LambdaExpressionSyntax)variableDeclaration.Declaration.Variables.Single().Initializer.Value;
 
            var variableDeclarationGroupOperation = (IVariableDeclarationGroupOperation)semanticModel.GetOperation(variableDeclaration);
            var variableTreeLambdaOperation = ((IDelegateCreationOperation)variableDeclarationGroupOperation.Declarations.Single().Declarators.Single().Initializer.Value).Target;
            var lambdaOperation = (IAnonymousFunctionOperation)semanticModel.GetOperation(lambdaSyntax);
 
            // Assert that both ways of getting to the lambda (requesting the lambda directly, and requesting via the lambda syntax)
            // return the same bound node.
            Assert.Same(variableTreeLambdaOperation, lambdaOperation);
 
            var variableDeclarationGroupOperationSecondRequest = (IVariableDeclarationGroupOperation)semanticModel.GetOperation(variableDeclaration);
            var variableTreeLambdaOperationSecondRequest = ((IDelegateCreationOperation)variableDeclarationGroupOperationSecondRequest.Declarations.Single().Declarators.Single().Initializer.Value).Target;
            var lambdaOperationSecondRequest = (IAnonymousFunctionOperation)semanticModel.GetOperation(lambdaSyntax);
 
            // Assert that, when request the variable declaration or the lambda for a second time, there is no rebinding of the
            // underlying UnboundLambda, and we get the same IAnonymousFunctionExpression as before
            Assert.Same(variableTreeLambdaOperation, variableTreeLambdaOperationSecondRequest);
            Assert.Same(lambdaOperation, lambdaOperationSecondRequest);
        }
 
        [CompilerTrait(CompilerFeature.IOperation)]
        [Fact]
        public void IAnonymousFunctionExpression_StaticLambda()
        {
            string source = @"
using System;
 
class Program
{
    static void Main(string[] args)
    {
        /*<bind>*/Action x = static () => F();/*</bind>*/
    }
 
    static void F()
    {
    }
}
";
            string expectedOperationTree = @"
IVariableDeclarationGroupOperation (1 declarations) (OperationKind.VariableDeclarationGroup, Type: null, IsInvalid) (Syntax: 'Action x =  ...  () => F();')
  IVariableDeclarationOperation (1 declarators) (OperationKind.VariableDeclaration, Type: null, IsInvalid) (Syntax: 'Action x =  ... c () => F()')
    Declarators:
        IVariableDeclaratorOperation (Symbol: System.Action x) (OperationKind.VariableDeclarator, Type: null, IsInvalid) (Syntax: 'x = static () => F()')
          Initializer: 
            IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null, IsInvalid) (Syntax: '= static () => F()')
              IDelegateCreationOperation (OperationKind.DelegateCreation, Type: System.Action, IsInvalid, IsImplicit) (Syntax: 'static () => F()')
                Target: 
                  IAnonymousFunctionOperation (Symbol: lambda expression) (OperationKind.AnonymousFunction, Type: null, IsInvalid) (Syntax: 'static () => F()')
                    IBlockOperation (2 statements) (OperationKind.Block, Type: null, IsImplicit) (Syntax: 'F()')
                      IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsImplicit) (Syntax: 'F()')
                        Expression: 
                          IInvocationOperation (void Program.F()) (OperationKind.Invocation, Type: System.Void) (Syntax: 'F()')
                            Instance Receiver: 
                              null
                            Arguments(0)
                      IReturnOperation (OperationKind.Return, Type: null, IsImplicit) (Syntax: 'F()')
                        ReturnedValue: 
                          null
    Initializer: 
      null
";
            var expectedDiagnostics = new DiagnosticDescription[] {
                // file.cs(8,30): error CS8400: Feature 'static anonymous function' is not available in C# 8.0. Please use language version 9.0 or greater.
                //         /*<bind>*/Action x = static () => F();/*</bind>*/
                Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion8, "static").WithArguments("static anonymous function", "9.0").WithLocation(8, 30)
            };
 
            VerifyOperationTreeAndDiagnosticsForTest<LocalDeclarationStatementSyntax>(
                source,
                expectedOperationTree,
                expectedDiagnostics,
                parseOptions: TestOptions.Regular8
                );
 
            var compilation = CreateCompilationWithMscorlib40AndSystemCore(source);
            var syntaxTree = compilation.SyntaxTrees[0];
            var semanticModel = compilation.GetSemanticModel(syntaxTree);
 
            var variableDeclaration = syntaxTree.GetRoot().DescendantNodes().OfType<LocalDeclarationStatementSyntax>().Single();
            var lambdaSyntax = (LambdaExpressionSyntax)variableDeclaration.Declaration.Variables.Single().Initializer.Value;
            var lambdaOperation = (IAnonymousFunctionOperation)semanticModel.GetOperation(lambdaSyntax);
 
            Assert.True(lambdaOperation.Symbol.IsStatic);
        }
 
        [CompilerTrait(CompilerFeature.IOperation)]
        [Fact]
        public void IAnonymousFunctionExpression_DefaultParameterValue()
        {
            var source = """
                class C
                {
                    void M()
                    {
                        const int N = 10;
                        var lam = (/*<bind>*/int x = N/*</bind>*/) => x;
                        lam();
                    }
                }
                """;
            var expectedOperationTree = """
                IParameterInitializerOperation (Parameter: [System.Int32 x = 10]) (OperationKind.ParameterInitializer, Type: null) (Syntax: '= N')
                  ILocalReferenceOperation: N (OperationKind.LocalReference, Type: System.Int32, Constant: 10) (Syntax: 'N')
                """;
            var expectedDiagnostics = DiagnosticDescription.None;
            VerifyOperationTreeAndDiagnosticsForTest<EqualsValueClauseSyntax>(source, expectedOperationTree, expectedDiagnostics);
        }
 
        [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)]
        [Fact]
        public void LambdaFlow_01()
        {
            string source = @"
struct C
{
    void M(System.Action<bool, bool> d)
/*<bind>*/{
        d = (bool result, bool input) =>
        {
            result = input;
        };
 
        d(false, true);
    }/*</bind>*/
}
";
            string expectedGraph = @"
Block[B0] - Entry
    Statements (0)
    Next (Regular) Block[B1]
Block[B1] - Block
    Predecessors: [B0]
    Statements (2)
        IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'd = (bool r ... };')
          Expression: 
            ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Action<System.Boolean, System.Boolean>) (Syntax: 'd = (bool r ... }')
              Left: 
                IParameterReferenceOperation: d (OperationKind.ParameterReference, Type: System.Action<System.Boolean, System.Boolean>) (Syntax: 'd')
              Right: 
                IDelegateCreationOperation (OperationKind.DelegateCreation, Type: System.Action<System.Boolean, System.Boolean>, IsImplicit) (Syntax: '(bool resul ... }')
                  Target: 
                    IFlowAnonymousFunctionOperation (Symbol: lambda expression) (OperationKind.FlowAnonymousFunction, Type: null) (Syntax: '(bool resul ... }')
                    {
                        Block[B0#A0] - Entry
                            Statements (0)
                            Next (Regular) Block[B1#A0]
                        Block[B1#A0] - Block
                            Predecessors: [B0#A0]
                            Statements (1)
                                IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'result = input;')
                                  Expression: 
                                    ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'result = input')
                                      Left: 
                                        IParameterReferenceOperation: result (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'result')
                                      Right: 
                                        IParameterReferenceOperation: input (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'input')
 
                            Next (Regular) Block[B2#A0]
                        Block[B2#A0] - Exit
                            Predecessors: [B1#A0]
                            Statements (0)
                    }
 
        IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'd(false, true);')
          Expression: 
            IInvocationOperation (virtual void System.Action<System.Boolean, System.Boolean>.Invoke(System.Boolean arg1, System.Boolean arg2)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'd(false, true)')
              Instance Receiver: 
                IParameterReferenceOperation: d (OperationKind.ParameterReference, Type: System.Action<System.Boolean, System.Boolean>) (Syntax: 'd')
              Arguments(2):
                  IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: arg1) (OperationKind.Argument, Type: null) (Syntax: 'false')
                    ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: False) (Syntax: 'false')
                    InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
                    OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
                  IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: arg2) (OperationKind.Argument, Type: null) (Syntax: 'true')
                    ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true')
                    InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
                    OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
 
    Next (Regular) Block[B2]
Block[B2] - Exit
    Predecessors: [B1]
    Statements (0)
";
            var expectedDiagnostics = DiagnosticDescription.None;
 
            VerifyFlowGraphAndDiagnosticsForTest<BlockSyntax>(source, expectedGraph, expectedDiagnostics);
        }
 
        [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)]
        [Fact]
        public void LambdaFlow_02()
        {
            string source = @"
struct C
{
    void M(System.Action<bool, bool> d1, System.Action<bool, bool> d2)
/*<bind>*/{
        d1 = (bool result1, bool input1) =>
        {
            result1 = input1;
        };
        d2 = (bool result2, bool input2) => result2 = input2;
    }/*</bind>*/
}
";
            string expectedGraph = @"
Block[B0] - Entry
    Statements (0)
    Next (Regular) Block[B1]
Block[B1] - Block
    Predecessors: [B0]
    Statements (2)
        IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'd1 = (bool  ... };')
          Expression: 
            ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Action<System.Boolean, System.Boolean>) (Syntax: 'd1 = (bool  ... }')
              Left: 
                IParameterReferenceOperation: d1 (OperationKind.ParameterReference, Type: System.Action<System.Boolean, System.Boolean>) (Syntax: 'd1')
              Right: 
                IDelegateCreationOperation (OperationKind.DelegateCreation, Type: System.Action<System.Boolean, System.Boolean>, IsImplicit) (Syntax: '(bool resul ... }')
                  Target: 
                    IFlowAnonymousFunctionOperation (Symbol: lambda expression) (OperationKind.FlowAnonymousFunction, Type: null) (Syntax: '(bool resul ... }')
                    {
                        Block[B0#A0] - Entry
                            Statements (0)
                            Next (Regular) Block[B1#A0]
                        Block[B1#A0] - Block
                            Predecessors: [B0#A0]
                            Statements (1)
                                IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'result1 = input1;')
                                  Expression: 
                                    ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'result1 = input1')
                                      Left: 
                                        IParameterReferenceOperation: result1 (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'result1')
                                      Right: 
                                        IParameterReferenceOperation: input1 (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'input1')
 
                            Next (Regular) Block[B2#A0]
                        Block[B2#A0] - Exit
                            Predecessors: [B1#A0]
                            Statements (0)
                    }
 
        IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'd2 = (bool  ... 2 = input2;')
          Expression: 
            ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Action<System.Boolean, System.Boolean>) (Syntax: 'd2 = (bool  ... t2 = input2')
              Left: 
                IParameterReferenceOperation: d2 (OperationKind.ParameterReference, Type: System.Action<System.Boolean, System.Boolean>) (Syntax: 'd2')
              Right: 
                IDelegateCreationOperation (OperationKind.DelegateCreation, Type: System.Action<System.Boolean, System.Boolean>, IsImplicit) (Syntax: '(bool resul ... t2 = input2')
                  Target: 
                    IFlowAnonymousFunctionOperation (Symbol: lambda expression) (OperationKind.FlowAnonymousFunction, Type: null) (Syntax: '(bool resul ... t2 = input2')
                    {
                        Block[B0#A1] - Entry
                            Statements (0)
                            Next (Regular) Block[B1#A1]
                        Block[B1#A1] - Block
                            Predecessors: [B0#A1]
                            Statements (1)
                                IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsImplicit) (Syntax: 'result2 = input2')
                                  Expression: 
                                    ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'result2 = input2')
                                      Left: 
                                        IParameterReferenceOperation: result2 (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'result2')
                                      Right: 
                                        IParameterReferenceOperation: input2 (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'input2')
 
                            Next (Regular) Block[B2#A1]
                        Block[B2#A1] - Exit
                            Predecessors: [B1#A1]
                            Statements (0)
                    }
 
    Next (Regular) Block[B2]
Block[B2] - Exit
    Predecessors: [B1]
    Statements (0)
";
            var expectedDiagnostics = DiagnosticDescription.None;
 
            VerifyFlowGraphAndDiagnosticsForTest<BlockSyntax>(source, expectedGraph, expectedDiagnostics);
        }
 
        [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)]
        [Fact]
        public void LambdaFlow_03()
        {
            string source = @"
struct C
{
    void M(System.Action<int> d1, System.Action<bool> d2)
/*<bind>*/{
        int i = 0;
 
        d1 = (int input1) =>
        {
            input1 = 1;
            i++;
 
            d2 = (bool input2) =>
            {
                input2 = true;
                i++;
            };
        };
    }/*</bind>*/
}
";
            string expectedGraph = @"
Block[B0] - Entry
    Statements (0)
    Next (Regular) Block[B1]
        Entering: {R1}
 
.locals {R1}
{
    Locals: [System.Int32 i]
    Block[B1] - Block
        Predecessors: [B0]
        Statements (2)
            ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32, IsImplicit) (Syntax: 'i = 0')
              Left: 
                ILocalReferenceOperation: i (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32, IsImplicit) (Syntax: 'i = 0')
              Right: 
                ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0')
 
            IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'd1 = (int i ... };')
              Expression: 
                ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Action<System.Int32>) (Syntax: 'd1 = (int i ... }')
                  Left: 
                    IParameterReferenceOperation: d1 (OperationKind.ParameterReference, Type: System.Action<System.Int32>) (Syntax: 'd1')
                  Right: 
                    IDelegateCreationOperation (OperationKind.DelegateCreation, Type: System.Action<System.Int32>, IsImplicit) (Syntax: '(int input1 ... }')
                      Target: 
                        IFlowAnonymousFunctionOperation (Symbol: lambda expression) (OperationKind.FlowAnonymousFunction, Type: null) (Syntax: '(int input1 ... }')
                        {
                            Block[B0#A0] - Entry
                                Statements (0)
                                Next (Regular) Block[B1#A0]
                            Block[B1#A0] - Block
                                Predecessors: [B0#A0]
                                Statements (3)
                                    IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'input1 = 1;')
                                      Expression: 
                                        ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32) (Syntax: 'input1 = 1')
                                          Left: 
                                            IParameterReferenceOperation: input1 (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'input1')
                                          Right: 
                                            ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1')
 
                                    IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'i++;')
                                      Expression: 
                                        IIncrementOrDecrementOperation (Postfix) (OperationKind.Increment, Type: System.Int32) (Syntax: 'i++')
                                          Target: 
                                            ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i')
 
                                    IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'd2 = (bool  ... };')
                                      Expression: 
                                        ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Action<System.Boolean>) (Syntax: 'd2 = (bool  ... }')
                                          Left: 
                                            IParameterReferenceOperation: d2 (OperationKind.ParameterReference, Type: System.Action<System.Boolean>) (Syntax: 'd2')
                                          Right: 
                                            IDelegateCreationOperation (OperationKind.DelegateCreation, Type: System.Action<System.Boolean>, IsImplicit) (Syntax: '(bool input ... }')
                                              Target: 
                                                IFlowAnonymousFunctionOperation (Symbol: lambda expression) (OperationKind.FlowAnonymousFunction, Type: null) (Syntax: '(bool input ... }')
                                                {
                                                    Block[B0#A0#A0] - Entry
                                                        Statements (0)
                                                        Next (Regular) Block[B1#A0#A0]
                                                    Block[B1#A0#A0] - Block
                                                        Predecessors: [B0#A0#A0]
                                                        Statements (2)
                                                            IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'input2 = true;')
                                                              Expression: 
                                                                ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'input2 = true')
                                                                  Left: 
                                                                    IParameterReferenceOperation: input2 (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'input2')
                                                                  Right: 
                                                                    ILiteralOperation (OperationKind.Literal, Type: System.Boolean, Constant: True) (Syntax: 'true')
 
                                                            IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'i++;')
                                                              Expression: 
                                                                IIncrementOrDecrementOperation (Postfix) (OperationKind.Increment, Type: System.Int32) (Syntax: 'i++')
                                                                  Target: 
                                                                    ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i')
 
                                                        Next (Regular) Block[B2#A0#A0]
                                                    Block[B2#A0#A0] - Exit
                                                        Predecessors: [B1#A0#A0]
                                                        Statements (0)
                                                }
 
                                Next (Regular) Block[B2#A0]
                            Block[B2#A0] - Exit
                                Predecessors: [B1#A0]
                                Statements (0)
                        }
 
        Next (Regular) Block[B2]
            Leaving: {R1}
}
 
Block[B2] - Exit
    Predecessors: [B1]
    Statements (0)
";
            var expectedDiagnostics = DiagnosticDescription.None;
 
            VerifyFlowGraphAndDiagnosticsForTest<BlockSyntax>(source, expectedGraph, expectedDiagnostics);
        }
 
        [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)]
        [Fact]
        public void LambdaFlow_04()
        {
            string source = @"
struct C
{
    void M(System.Action d1, System.Action<bool, bool> d2)
/*<bind>*/{
        d1 = () =>
        {
            d2 = (bool result1, bool input1) =>
            {
                result1 = input1;
            
            };
        };
 
        void local(bool result2, bool input2) => result2 = input2;
    }/*</bind>*/
}
";
 
            var compilation = CreateCompilation(source);
            var tree = compilation.SyntaxTrees.Single();
            var semanticModel = compilation.GetSemanticModel(tree);
            var graphM = ControlFlowGraph.Create((IMethodBodyOperation)semanticModel.GetOperation(tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Single()));
 
            Assert.NotNull(graphM);
            Assert.Null(graphM.Parent);
 
            IFlowAnonymousFunctionOperation lambdaD1 = getLambda(graphM);
 
            Assert.Throws<ArgumentNullException>(() => graphM.GetLocalFunctionControlFlowGraph(null));
            Assert.Throws<ArgumentOutOfRangeException>(() => graphM.GetLocalFunctionControlFlowGraph(lambdaD1.Symbol));
            Assert.Throws<ArgumentNullException>(() => graphM.GetLocalFunctionControlFlowGraphInScope(null));
            Assert.Throws<ArgumentOutOfRangeException>(() => graphM.GetLocalFunctionControlFlowGraphInScope(lambdaD1.Symbol));
 
            var graphD1 = graphM.GetAnonymousFunctionControlFlowGraph(lambdaD1);
            Assert.NotNull(graphD1);
            Assert.Same(graphM, graphD1.Parent);
            var graphD1_FromExtension = graphM.GetAnonymousFunctionControlFlowGraphInScope(lambdaD1);
            Assert.Same(graphD1, graphD1_FromExtension);
 
            IFlowAnonymousFunctionOperation lambdaD2 = getLambda(graphD1);
            var graphD2 = graphD1.GetAnonymousFunctionControlFlowGraph(lambdaD2);
            Assert.NotNull(graphD2);
            Assert.Same(graphD1, graphD2.Parent);
 
            Assert.Throws<ArgumentNullException>(() => graphM.GetAnonymousFunctionControlFlowGraph(null));
            Assert.Throws<ArgumentOutOfRangeException>(() => graphM.GetAnonymousFunctionControlFlowGraph(lambdaD2));
            Assert.Throws<ArgumentNullException>(() => graphM.GetAnonymousFunctionControlFlowGraphInScope(null));
            Assert.Throws<ArgumentOutOfRangeException>(() => graphM.GetAnonymousFunctionControlFlowGraphInScope(lambdaD2));
 
            IFlowAnonymousFunctionOperation getLambda(ControlFlowGraph graph)
            {
                return graph.Blocks.SelectMany(b => b.Operations.SelectMany(o => o.DescendantsAndSelf())).OfType<IFlowAnonymousFunctionOperation>().Single();
            }
        }
 
        [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)]
        [Fact]
        public void LambdaFlow_05()
        {
            string source = @"
struct C
{
    void M(System.Action d1, System.Action d2)
/*<bind>*/{
        d1 = () => { };
        d2 = () =>
        {
            d1();
        };
    }/*</bind>*/
}
";
 
            var compilation = CreateCompilation(source);
            var tree = compilation.SyntaxTrees.Single();
            var semanticModel = compilation.GetSemanticModel(tree);
            var graphM = ControlFlowGraph.Create((IMethodBodyOperation)semanticModel.GetOperation(tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Single()));
 
            Assert.NotNull(graphM);
            Assert.Null(graphM.Parent);
 
            IFlowAnonymousFunctionOperation lambdaD1 = getLambda(graphM, index: 0);
            Assert.NotNull(lambdaD1);
            IFlowAnonymousFunctionOperation lambdaD2 = getLambda(graphM, index: 1);
            Assert.NotNull(lambdaD2);
 
            Assert.Throws<ArgumentNullException>(() => graphM.GetLocalFunctionControlFlowGraph(null));
            Assert.Throws<ArgumentOutOfRangeException>(() => graphM.GetLocalFunctionControlFlowGraph(lambdaD1.Symbol));
            Assert.Throws<ArgumentNullException>(() => graphM.GetLocalFunctionControlFlowGraphInScope(null));
            Assert.Throws<ArgumentOutOfRangeException>(() => graphM.GetLocalFunctionControlFlowGraphInScope(lambdaD1.Symbol));
 
            var graphD1 = graphM.GetAnonymousFunctionControlFlowGraph(lambdaD1);
            Assert.NotNull(graphD1);
            Assert.Same(graphM, graphD1.Parent);
            var graphD2 = graphM.GetAnonymousFunctionControlFlowGraph(lambdaD2);
            Assert.NotNull(graphD2);
            Assert.Same(graphM, graphD2.Parent);
 
            var graphD1_FromExtension = graphM.GetAnonymousFunctionControlFlowGraphInScope(lambdaD1);
            Assert.Same(graphD1, graphD1_FromExtension);
 
            Assert.Throws<ArgumentOutOfRangeException>(() => graphD2.GetAnonymousFunctionControlFlowGraph(lambdaD1));
            graphD1_FromExtension = graphD2.GetAnonymousFunctionControlFlowGraphInScope(lambdaD1);
            Assert.Same(graphD1, graphD1_FromExtension);
 
            IFlowAnonymousFunctionOperation getLambda(ControlFlowGraph graph, int index)
            {
                return graph.Blocks.SelectMany(b => b.Operations.SelectMany(o => o.DescendantsAndSelf())).OfType<IFlowAnonymousFunctionOperation>().ElementAt(index);
            }
        }
    }
}