Addressed PR feedback, updated ILocalFunctionOperation to final design specified in review.

上级 06dcac6a
......@@ -492,28 +492,29 @@ private BoundStatement BindLocalFunctionStatement(LocalFunctionStatementSyntax n
var hasErrors = localSymbol.ScopeBinder
.ValidateDeclarationNameConflictsInScope(localSymbol, diagnostics);
BoundBlock blockBody = node.Body != null ? BindEmbeddedBlock(node.Body, diagnostics) : null;
DiagnosticBag expressionBodyDiagnostics = null;
BoundBlock blockBody = null;
BoundBlock expressionBody = null;
if (node.ExpressionBody != null)
if (node.Body != null)
{
expressionBodyDiagnostics = node.Body == null ? diagnostics : new DiagnosticBag();
expressionBody = BindExpressionBodyAsBlock(node.ExpressionBody, expressionBodyDiagnostics);
blockBody = runAnalysis(BindEmbeddedBlock(node.Body, diagnostics), diagnostics);
if (node.ExpressionBody != null)
{
var expressionBodyDiagnostics = new DiagnosticBag();
expressionBody = runAnalysis(BindExpressionBodyAsBlock(node.ExpressionBody, expressionBodyDiagnostics), expressionBodyDiagnostics);
}
}
else if (node.Body == null && node.ExpressionBody == null)
else if (node.ExpressionBody != null)
{
expressionBody = runAnalysis(BindExpressionBodyAsBlock(node.ExpressionBody, diagnostics), diagnostics);
}
else
{
hasErrors = true;
diagnostics.Add(ErrorCode.ERR_LocalFunctionMissingBody, localSymbol.Locations[0], localSymbol);
}
Debug.Assert(blockBody != null || expressionBody != null || hasErrors);
Debug.Assert((expressionBody == null) == (expressionBodyDiagnostics == null));
if (blockBody != null || expressionBody != null)
{
localSymbol.ComputeReturnType();
runAnalysis(ref blockBody, diagnostics);
runAnalysis(ref expressionBody, expressionBodyDiagnostics);
}
Debug.Assert(blockBody != null || expressionBody != null || hasErrors);
localSymbol.GetDeclarationDiagnostics(diagnostics);
......@@ -522,7 +523,7 @@ private BoundStatement BindLocalFunctionStatement(LocalFunctionStatementSyntax n
return new BoundLocalFunctionStatement(node, localSymbol, blockBody, expressionBody, hasErrors);
void runAnalysis(ref BoundBlock block, DiagnosticBag blockDiagnostics)
BoundBlock runAnalysis(BoundBlock block, DiagnosticBag blockDiagnostics)
{
if (block != null)
{
......@@ -543,6 +544,8 @@ void runAnalysis(ref BoundBlock block, DiagnosticBag blockDiagnostics)
}
}
}
return block;
}
}
......
......@@ -21,11 +21,6 @@ internal partial class BoundBadStatement
protected override ImmutableArray<BoundNode> Children => this.ChildBoundNodes;
}
partial class BoundLocalFunctionStatement
{
protected override ImmutableArray<BoundNode> Children => ImmutableArray.Create<BoundNode>(this.BlockBody, this.ExpressionBody);
}
partial class BoundFixedStatement
{
protected override ImmutableArray<BoundNode> Children => ImmutableArray.Create<BoundNode>(this.Declarations, this.Body);
......
......@@ -29,7 +29,7 @@ internal sealed partial class BoundLocalFunctionStatement : IBoundLambdaOrFuncti
SyntaxNode IBoundLambdaOrFunction.Syntax { get { return Syntax; } }
BoundBlock IBoundLambdaOrFunction.Body { get => BlockBody ?? ExpressionBody; }
BoundBlock IBoundLambdaOrFunction.Body { get => this.Body; }
}
internal sealed partial class BoundLambda : IBoundLambdaOrFunction
......
......@@ -1396,7 +1396,15 @@ private CSharpSyntaxNode GetBindingRoot(CSharpSyntaxNode node)
{
case SyntaxKind.ThisConstructorInitializer:
case SyntaxKind.BaseConstructorInitializer:
return current;
case SyntaxKind.ArrowExpressionClause:
// If this is an arrow expression on a local function statement, then our bindable root is actually our parent syntax as it's
// a statement in a function. If this is returned directly in IOperation, we'll end up with a separate tree.
if (current.ParentOrStructuredTriviaParent != null && current.ParentOrStructuredTriviaParent.Kind() == SyntaxKind.LocalFunctionStatement)
{
return current.ParentOrStructuredTriviaParent;
}
return current;
}
}
......
......@@ -36,8 +36,7 @@ public override BoundNode VisitLambda(BoundLambda node)
public override BoundNode VisitLocalFunctionStatement(BoundLocalFunctionStatement node)
{
VisitLocalFunctionOrLambda(node.BlockBody);
VisitLocalFunctionOrLambda(node.ExpressionBody);
VisitLocalFunctionOrLambda(node.Body);
return null;
}
......
......@@ -156,7 +156,7 @@ public override BoundNode VisitLocalFunctionStatement(BoundLocalFunctionStatemen
PendingBranches.Add(new PendingBranch(null, this.State));
}
VisitAlways(localFunc.BlockBody ?? localFunc.ExpressionBody);
VisitAlways(localFunc.Body);
RestorePending(oldPending2); // process any forward branches within the lambda body
ImmutableArray<PendingBranch> pendingReturns = RemoveReturns();
RestorePending(oldPending);
......
......@@ -386,7 +386,7 @@ public override BoundNode VisitLambda(BoundLambda node)
}
public override BoundNode VisitLocalFunctionStatement(BoundLocalFunctionStatement node)
=> VisitClosure(node.Symbol.OriginalDefinition, node.BlockBody ?? node.ExpressionBody);
=> VisitClosure(node.Symbol.OriginalDefinition, node.Body);
public override BoundNode VisitCall(BoundCall node)
{
......
......@@ -774,13 +774,15 @@ private IAnonymousFunctionOperation CreateBoundLambdaOperation(BoundLambda bound
private ILocalFunctionOperation CreateBoundLocalFunctionStatementOperation(BoundLocalFunctionStatement boundLocalFunctionStatement)
{
IMethodSymbol symbol = boundLocalFunctionStatement.Symbol;
Lazy<IBlockOperation> blockBody = new Lazy<IBlockOperation>(() => (IBlockOperation)Create(boundLocalFunctionStatement.BlockBody));
Lazy<IBlockOperation> expressionBody = new Lazy<IBlockOperation>(() => (IBlockOperation)Create(boundLocalFunctionStatement.ExpressionBody));
Lazy<IBlockOperation> body = new Lazy<IBlockOperation>(() => (IBlockOperation)Create(boundLocalFunctionStatement.Body));
Lazy<IBlockOperation> ignoredBody = new Lazy<IBlockOperation>(() => boundLocalFunctionStatement.BlockBody != null && boundLocalFunctionStatement.ExpressionBody != null ?
(IBlockOperation)Create(boundLocalFunctionStatement.ExpressionBody) :
null);
SyntaxNode syntax = boundLocalFunctionStatement.Syntax;
ITypeSymbol type = null;
Optional<object> constantValue = default(Optional<object>);
bool isImplicit = boundLocalFunctionStatement.WasCompilerGenerated;
return new LazyLocalFunctionStatement(symbol, blockBody, expressionBody, _semanticModel, syntax, type, constantValue, isImplicit);
return new LazyLocalFunctionStatement(symbol, body, ignoredBody, _semanticModel, syntax, type, constantValue, isImplicit);
}
private IOperation CreateBoundConversionOperation(BoundConversion boundConversion)
......
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Operations;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using System;
......@@ -90,10 +91,12 @@ static void Main(string[] args)
}");
var tree = comp.SyntaxTrees.Single();
var model = comp.GetSemanticModel(tree, ignoreAccessibility: false);
var creation = tree.GetRoot().DescendantNodes().OfType<ObjectCreationExpressionSyntax>().Single();
var localFunction = tree.GetRoot().DescendantNodes().OfType<LocalFunctionStatementSyntax>().Single();
var creation = localFunction.DescendantNodes().OfType<ObjectCreationExpressionSyntax>().Single();
var operation = model.GetOperation(creation);
Assert.NotNull(operation);
var objectCreationOperation = model.GetOperation(creation);
var localFunctionOperation = (ILocalFunctionOperation)model.GetOperation(localFunction);
Assert.NotNull(objectCreationOperation);
comp.VerifyOperationTree(creation, expectedOperationTree:
@"
......@@ -103,11 +106,9 @@ static void Main(string[] args)
null
");
Assert.Equal(OperationKind.ExpressionStatement, operation.Parent.Kind);
Assert.Equal(OperationKind.Block, operation.Parent.Parent.Kind);
// We didn't bind the expression body, but should. See issue https://github.com/dotnet/roslyn/issues/24650
// The block from the previous assert, should have a parent
Assert.Null(operation.Parent.Parent.Parent);
Assert.Equal(OperationKind.ExpressionStatement, objectCreationOperation.Parent.Kind);
Assert.Equal(OperationKind.Block, objectCreationOperation.Parent.Parent.Kind);
Assert.Equal(localFunctionOperation.IgnoredBody, objectCreationOperation.Parent.Parent);
var info = model.GetTypeInfo(creation);
Assert.Equal("System.Object", info.Type.ToTestDisplayString());
......
......@@ -1038,40 +1038,39 @@ static IEnumerable<T> MyIterator<T>(IEnumerable<T> source, Func<T, bool> predica
";
string expectedOperationTree = @"
ILocalFunctionOperation (Symbol: System.Collections.Generic.IEnumerable<T> Iterator()) (OperationKind.LocalFunction, Type: null) (Syntax: 'IEnumerable ... }')
Block Body:
IBlockOperation (2 statements) (OperationKind.Block, Type: null) (Syntax: '{ ... }')
IForEachLoopOperation (LoopKind.ForEach) (OperationKind.Loop, Type: null) (Syntax: 'foreach (va ... rn element;')
Locals: Local_1: T element
LoopControlVariable:
IVariableDeclaratorOperation (Symbol: T element) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'var')
Initializer:
null
Collection:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.IEnumerable<T>, IsImplicit) (Syntax: 'source')
Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Operand:
IParameterReferenceOperation: source (OperationKind.ParameterReference, Type: System.Collections.Generic.IEnumerable<T>) (Syntax: 'source')
Body:
IConditionalOperation (OperationKind.Conditional, Type: null) (Syntax: 'if (predica ... rn element;')
Condition:
IInvocationOperation (virtual System.Boolean System.Func<T, System.Boolean>.Invoke(T arg)) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'predicate(element)')
Instance Receiver:
IParameterReferenceOperation: predicate (OperationKind.ParameterReference, Type: System.Func<T, System.Boolean>) (Syntax: 'predicate')
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: arg) (OperationKind.Argument, Type: null) (Syntax: 'element')
ILocalReferenceOperation: element (OperationKind.LocalReference, Type: T) (Syntax: 'element')
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)
WhenTrue:
IReturnOperation (OperationKind.YieldReturn, Type: null) (Syntax: 'yield return element;')
ReturnedValue:
ILocalReferenceOperation: element (OperationKind.LocalReference, Type: T) (Syntax: 'element')
WhenFalse:
null
NextVariables(0)
IReturnOperation (OperationKind.YieldBreak, Type: null, IsImplicit) (Syntax: '{ ... }')
ReturnedValue:
null
IBlockOperation (2 statements) (OperationKind.Block, Type: null) (Syntax: '{ ... }')
IForEachLoopOperation (LoopKind.ForEach) (OperationKind.Loop, Type: null) (Syntax: 'foreach (va ... rn element;')
Locals: Local_1: T element
LoopControlVariable:
IVariableDeclaratorOperation (Symbol: T element) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'var')
Initializer:
null
Collection:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.IEnumerable<T>, IsImplicit) (Syntax: 'source')
Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Operand:
IParameterReferenceOperation: source (OperationKind.ParameterReference, Type: System.Collections.Generic.IEnumerable<T>) (Syntax: 'source')
Body:
IConditionalOperation (OperationKind.Conditional, Type: null) (Syntax: 'if (predica ... rn element;')
Condition:
IInvocationOperation (virtual System.Boolean System.Func<T, System.Boolean>.Invoke(T arg)) (OperationKind.Invocation, Type: System.Boolean) (Syntax: 'predicate(element)')
Instance Receiver:
IParameterReferenceOperation: predicate (OperationKind.ParameterReference, Type: System.Func<T, System.Boolean>) (Syntax: 'predicate')
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: arg) (OperationKind.Argument, Type: null) (Syntax: 'element')
ILocalReferenceOperation: element (OperationKind.LocalReference, Type: T) (Syntax: 'element')
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)
WhenTrue:
IReturnOperation (OperationKind.YieldReturn, Type: null) (Syntax: 'yield return element;')
ReturnedValue:
ILocalReferenceOperation: element (OperationKind.LocalReference, Type: T) (Syntax: 'element')
WhenFalse:
null
NextVariables(0)
IReturnOperation (OperationKind.YieldBreak, Type: null, IsImplicit) (Syntax: '{ ... }')
ReturnedValue:
null
";
var expectedDiagnostics = DiagnosticDescription.None;
......
......@@ -5947,28 +5947,23 @@ internal abstract partial class BaseLocalFunctionStatement : Operation, ILocalFu
/// Local function symbol.
/// </summary>
public IMethodSymbol Symbol { get; }
protected abstract IBlockOperation BlockBodyImpl { get; }
protected abstract IBlockOperation ExpressionBodyImpl { get; }
public override IEnumerable<IOperation> Children
{
get
{
if (BlockBody != null)
if (Body != null)
{
yield return BlockBody;
yield return Body;
}
if (ExpressionBody != null)
if (IgnoredBody != null)
{
yield return ExpressionBody;
yield return IgnoredBody;
}
}
}
/// <summary>
/// Body of the local function.
/// </summary>
public IBlockOperation Body => BlockBody ?? ExpressionBody;
public IBlockOperation BlockBody => Operation.SetParentOperation(BlockBodyImpl, this);
public IBlockOperation ExpressionBody => Operation.SetParentOperation(ExpressionBodyImpl, this);
public abstract IBlockOperation Body { get; }
public abstract IBlockOperation IgnoredBody { get; }
public override void Accept(OperationVisitor visitor)
{
visitor.VisitLocalFunction(this);
......@@ -5984,15 +5979,15 @@ public override void Accept(OperationVisitor visitor)
/// </summary>
internal sealed partial class LocalFunctionStatement : BaseLocalFunctionStatement, ILocalFunctionOperation
{
public LocalFunctionStatement(IMethodSymbol symbol, IBlockOperation blockBody, IBlockOperation expressionBody, SemanticModel semanticModel, SyntaxNode syntax, ITypeSymbol type, Optional<object> constantValue, bool isImplicit) :
public LocalFunctionStatement(IMethodSymbol symbol, IBlockOperation body, IBlockOperation ignoredBody, SemanticModel semanticModel, SyntaxNode syntax, ITypeSymbol type, Optional<object> constantValue, bool isImplicit) :
base(symbol, semanticModel, syntax, type, constantValue, isImplicit)
{
BlockBodyImpl = blockBody;
ExpressionBodyImpl = expressionBody;
Body = SetParentOperation<IBlockOperation>(body, this);
IgnoredBody = SetParentOperation<IBlockOperation>(ignoredBody, this);
}
protected override IBlockOperation BlockBodyImpl { get; }
protected override IBlockOperation ExpressionBodyImpl { get; }
public override IBlockOperation Body { get; }
public override IBlockOperation IgnoredBody { get; }
}
/// <summary>
......@@ -6000,18 +5995,18 @@ internal sealed partial class LocalFunctionStatement : BaseLocalFunctionStatemen
/// </summary>
internal sealed partial class LazyLocalFunctionStatement : BaseLocalFunctionStatement, ILocalFunctionOperation
{
private readonly Lazy<IBlockOperation> _lazyBlockBody;
private readonly Lazy<IBlockOperation> _lazyExpressionBody;
private readonly Lazy<IBlockOperation> _lazyBody;
private readonly Lazy<IBlockOperation> _lazyIgnoredBody;
public LazyLocalFunctionStatement(IMethodSymbol symbol, Lazy<IBlockOperation> blockBody, Lazy<IBlockOperation> expressionBody, SemanticModel semanticModel, SyntaxNode syntax, ITypeSymbol type, Optional<object> constantValue, bool isImplicit)
public LazyLocalFunctionStatement(IMethodSymbol symbol, Lazy<IBlockOperation> body, Lazy<IBlockOperation> ignoredBody, SemanticModel semanticModel, SyntaxNode syntax, ITypeSymbol type, Optional<object> constantValue, bool isImplicit)
: base(symbol, semanticModel, syntax, type, constantValue, isImplicit)
{
_lazyBlockBody = blockBody ?? throw new System.ArgumentNullException(nameof(blockBody));
_lazyExpressionBody = expressionBody ?? throw new System.ArgumentNullException(nameof(expressionBody));
_lazyBody = body ?? throw new System.ArgumentNullException(nameof(body));
_lazyIgnoredBody = ignoredBody ?? throw new System.ArgumentNullException(nameof(ignoredBody));
}
protected override IBlockOperation BlockBodyImpl => _lazyBlockBody.Value;
protected override IBlockOperation ExpressionBodyImpl => _lazyExpressionBody.Value;
public override IBlockOperation Body => SetParentOperation(_lazyBody.Value, this);
public override IBlockOperation IgnoredBody => SetParentOperation(_lazyIgnoredBody.Value, this);
}
/// <summary>
......
......@@ -22,19 +22,14 @@ public interface ILocalFunctionOperation : IOperation
/// <summary>
/// Body of the local function.
/// </summary>
/// <remarks>
/// This will return the <see cref="BlockBody"/> if it exists, and the <see cref="ExpressionBody"/> if the <see cref="BlockBody"/> does not exist.
/// If both exist, this will return just the <see cref="BlockBody"/>, and you must use <see cref="ExpressionBody"/> to retrieve the expression body.
/// </remarks>
IBlockOperation Body { get; }
/// <summary>
/// The block body of the local function, if it exists.
/// </summary>
IBlockOperation BlockBody { get; }
/// <summary>
/// The expression body of the local function, if it exists.
/// An extra body for the local function, if both a block body and expression body are specified in source.
/// </summary>
IBlockOperation ExpressionBody { get; }
/// <remarks>
/// This is only ever non-null in error situations.
/// </remarks>
IBlockOperation IgnoredBody { get; }
}
}
......@@ -438,7 +438,7 @@ public override IOperation VisitInvalid(IInvalidOperation operation, object argu
public override IOperation VisitLocalFunction(ILocalFunctionOperation operation, object argument)
{
return new LocalFunctionStatement(operation.Symbol, Visit(operation.BlockBody), Visit(operation.ExpressionBody), ((Operation)operation).SemanticModel, operation.Syntax, operation.Type, operation.ConstantValue, operation.IsImplicit);
return new LocalFunctionStatement(operation.Symbol, Visit(operation.Body), Visit(operation.IgnoredBody), ((Operation)operation).SemanticModel, operation.Syntax, operation.Type, operation.ConstantValue, operation.IsImplicit);
}
public override IOperation VisitInterpolatedString(IInterpolatedStringOperation operation, object argument)
......
......@@ -16,8 +16,7 @@ Microsoft.CodeAnalysis.OperationKind.TupleBinaryOperator = 87 -> Microsoft.CodeA
Microsoft.CodeAnalysis.Operations.IConstructorBodyOperation
Microsoft.CodeAnalysis.Operations.IConstructorBodyOperation.Initializer.get -> Microsoft.CodeAnalysis.IOperation
Microsoft.CodeAnalysis.Operations.IConstructorBodyOperation.Locals.get -> System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.ILocalSymbol>
Microsoft.CodeAnalysis.Operations.ILocalFunctionOperation.BlockBody.get -> Microsoft.CodeAnalysis.Operations.IBlockOperation
Microsoft.CodeAnalysis.Operations.ILocalFunctionOperation.ExpressionBody.get -> Microsoft.CodeAnalysis.Operations.IBlockOperation
Microsoft.CodeAnalysis.Operations.ILocalFunctionOperation.IgnoredBody.get -> Microsoft.CodeAnalysis.Operations.IBlockOperation
Microsoft.CodeAnalysis.Operations.IMethodBodyBaseOperation
Microsoft.CodeAnalysis.Operations.IMethodBodyBaseOperation.BlockBody.get -> Microsoft.CodeAnalysis.Operations.IBlockOperation
Microsoft.CodeAnalysis.Operations.IMethodBodyBaseOperation.ExpressionBody.get -> Microsoft.CodeAnalysis.Operations.IBlockOperation
......
......@@ -1423,13 +1423,18 @@ public override void VisitLocalFunction(ILocalFunctionOperation operation)
LogString(")");
LogCommonPropertiesAndNewLine(operation);
if (operation.BlockBody != null)
if (operation.Body != null)
{
Visit(operation.BlockBody, "Block Body");
}
if (operation.ExpressionBody != null)
{
Visit(operation.ExpressionBody, "Expression Body");
if (operation.IgnoredBody != null)
{
Visit(operation.Body, "Body");
Visit(operation.IgnoredBody, "IgnoredBody");
}
else
{
Visit(operation.Body);
}
}
}
......
......@@ -717,29 +717,21 @@ public override void VisitLocalFunction(ILocalFunctionOperation operation)
if (operation.Body != null)
{
if (operation.BlockBody == null)
var children = operation.Children.ToImmutableArray();
Assert.Same(operation.Body, children[0]);
if (operation.IgnoredBody != null)
{
Assert.Same(operation.ExpressionBody, operation.Body);
Assert.Same(operation.ExpressionBody, operation.Children.Single());
Assert.Same(operation.IgnoredBody, children[1]);
Assert.Equal(2, children.Length);
}
else
{
Assert.Same(operation.BlockBody, operation.Body);
if (operation.ExpressionBody != null)
{
ImmutableArray<IOperation> children = operation.Children.ToImmutableArray();
Assert.Equal(2, children.Length);
Assert.Same(operation.BlockBody, children[0]);
Assert.Same(operation.ExpressionBody, children[1]);
}
else
{
Assert.Same(operation.BlockBody, operation.Children.Single());
}
Assert.Equal(1, children.Length);
}
}
else
{
Assert.Null(operation.IgnoredBody);
Assert.Empty(operation.Children);
}
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册