// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Semantics;
using Microsoft.CodeAnalysis.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.CSharp.UnitTests
{
public partial class IOperationTests : SemanticModelTestBase
{
[CompilerTrait(CompilerFeature.IOperation)]
[Fact]
public void IAnonymousFunctionExpression_BoundLambda_HasValidLambdaExpressionTree()
{
string source = @"
using System;
class Program
{
static void Main(string[] args)
{
/**/Action x = () => F();/**/
}
static void F()
{
}
}
";
string expectedOperationTree = @"
IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement) (Syntax: 'Action x = () => F();')
IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration) (Syntax: 'x = () => F()')
Variables: Local_1: System.Action x
Initializer: IConversionExpression (Implicit, TryCast: False, Unchecked) (OperationKind.ConversionExpression, Type: System.Action) (Syntax: '() => F()')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Operand: IAnonymousFunctionExpression (Symbol: lambda expression) (OperationKind.AnonymousFunctionExpression, Type: null) (Syntax: '() => F()')
IBlockStatement (2 statements) (OperationKind.BlockStatement) (Syntax: 'F()')
IExpressionStatement (OperationKind.ExpressionStatement) (Syntax: 'F()')
Expression: IInvocationExpression (void Program.F()) (OperationKind.InvocationExpression, Type: System.Void) (Syntax: 'F()')
Instance Receiver: null
Arguments(0)
IReturnStatement (OperationKind.ReturnStatement) (Syntax: 'F()')
ReturnedValue: null
";
var expectedDiagnostics = DiagnosticDescription.None;
VerifyOperationTreeAndDiagnosticsForTest(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 = /**/() => F()/**/;
}
static void F()
{
}
}
";
string expectedOperationTree = @"
IAnonymousFunctionExpression (Symbol: lambda expression) (OperationKind.AnonymousFunctionExpression, Type: null) (Syntax: '() => F()')
IBlockStatement (2 statements) (OperationKind.BlockStatement) (Syntax: 'F()')
IExpressionStatement (OperationKind.ExpressionStatement) (Syntax: 'F()')
Expression: IInvocationExpression (void Program.F()) (OperationKind.InvocationExpression, Type: System.Void) (Syntax: 'F()')
Instance Receiver: null
Arguments(0)
IReturnStatement (OperationKind.ReturnStatement) (Syntax: 'F()')
ReturnedValue: null
";
var expectedDiagnostics = DiagnosticDescription.None;
VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics);
}
[CompilerTrait(CompilerFeature.IOperation)]
[Fact]
public void IAnonymousFunctionExpression_UnboundLambdaAsVar_HasValidLambdaExpressionTree()
{
string source = @"
using System;
class Program
{
static void Main(string[] args)
{
/**/var x = () => F();/**/
}
static void F()
{
}
}
";
string expectedOperationTree = @"
IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement, IsInvalid) (Syntax: 'var x = () => F();')
IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration, IsInvalid) (Syntax: 'x = () => F()')
Variables: Local_1: var x
Initializer: IAnonymousFunctionExpression (Symbol: lambda expression) (OperationKind.AnonymousFunctionExpression, Type: null, IsInvalid) (Syntax: '() => F()')
IBlockStatement (1 statements) (OperationKind.BlockStatement, IsInvalid) (Syntax: 'F()')
IExpressionStatement (OperationKind.ExpressionStatement, IsInvalid) (Syntax: 'F()')
Expression: IInvocationExpression (void Program.F()) (OperationKind.InvocationExpression, Type: System.Void, IsInvalid) (Syntax: 'F()')
Instance Receiver: null
Arguments(0)
";
var expectedDiagnostics = new DiagnosticDescription[] {
// CS0815: Cannot assign lambda expression to an implicitly-typed variable
// /**/var x = () => F();/**/
Diagnostic(ErrorCode.ERR_ImplicitlyTypedVariableAssignedBadValue, "x = () => F()").WithArguments("lambda expression").WithLocation(8, 23),
};
VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics);
}
[CompilerTrait(CompilerFeature.IOperation)]
[Fact]
public void IAnonymousFunctionExpression_UnboundLambdaAsDelegate_HasValidLambdaExpressionTree()
{
string source = @"
using System;
class Program
{
static void Main(string[] args)
{
/**/Action x = () => F();/**/
}
static void F()
{
}
}
";
string expectedOperationTree = @"
IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement, IsInvalid) (Syntax: 'Action ... () => F();')
IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration, IsInvalid) (Syntax: 'x = () => F()')
Variables: Local_1: System.Action x
Initializer: IConversionExpression (Implicit, TryCast: False, Unchecked) (OperationKind.ConversionExpression, Type: System.Action, IsInvalid) (Syntax: '() => F()')
Conversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Operand: IAnonymousFunctionExpression (Symbol: lambda expression) (OperationKind.AnonymousFunctionExpression, Type: null, IsInvalid) (Syntax: '() => F()')
IBlockStatement (1 statements) (OperationKind.BlockStatement, IsInvalid) (Syntax: 'F()')
IExpressionStatement (OperationKind.ExpressionStatement, IsInvalid) (Syntax: 'F()')
Expression: IInvocationExpression (void Program.F()) (OperationKind.InvocationExpression, Type: System.Void, IsInvalid) (Syntax: 'F()')
Instance Receiver: null
Arguments(0)
";
var expectedDiagnostics = new DiagnosticDescription[] {
// CS1593: Delegate 'Action' does not take 0 arguments
// Action x /**/= () => F()/**/;
Diagnostic(ErrorCode.ERR_BadDelArgCount, "() => F()").WithArguments("System.Action", "0").WithLocation(8, 35)
};
VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics);
}
[CompilerTrait(CompilerFeature.IOperation)]
[Fact]
public void IAnonymousFunctionExpression_UnboundLambda_ReferenceEquality()
{
string source = @"
using System;
class Program
{
static void Main(string[] args)
{
/**/var x = () => F();/**/
}
static void F()
{
}
}
";
var compilation = CreateCompilationWithMscorlibAndSystemCore(source);
var syntaxTree = compilation.SyntaxTrees[0];
var semanticModel = compilation.GetSemanticModel(syntaxTree);
var variableDeclaration = syntaxTree.GetRoot().DescendantNodes().OfType().Single();
var lambdaSyntax = (LambdaExpressionSyntax)variableDeclaration.Declaration.Variables.Single().Initializer.Value;
var variableDeclarationOperation = (IVariableDeclarationStatement)semanticModel.GetOperationInternal(variableDeclaration);
var variableTreeLambdaOperation = (IAnonymousFunctionExpression)variableDeclarationOperation.Declarations.Single().Initializer;
var lambdaOperation = (IAnonymousFunctionExpression)semanticModel.GetOperationInternal(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 variableDeclarationOperationSecondRequest = (IVariableDeclarationStatement)semanticModel.GetOperationInternal(variableDeclaration);
var variableTreeLambdaOperationSecondRequest = (IAnonymousFunctionExpression)variableDeclarationOperation.Declarations.Single().Initializer;
var lambdaOperationSecondRequest = (IAnonymousFunctionExpression)semanticModel.GetOperationInternal(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);
}
}
}