提交 388d11a4 编写于 作者: E Evan Hauck

More local function features working

上级 927b461e
......@@ -416,25 +416,43 @@ private BoundStatement BindLocalFunctionStatement(LocalFunctionStatementSyntax n
var hasErrors = false;
hasErrors |= this.ValidateDeclarationNameConflictsInScope(localSymbol, diagnostics);
var inMethodBinder = new InMethodBinder(localSymbol, this);
var blockBinder = new ExecutableCodeBinder(node, localSymbol, inMethodBinder);
BoundBlock block;
if (node.Body != null)
// In error scenarios with misplaced code, it is possible we can't bind the local declaration.
// This occurs through the semantic model. In that case concoct a plausible result.
if (localSymbol == null)
{
block = blockBinder.BindBlock(node.Body, diagnostics);
localSymbol = new LocalFunctionMethodSymbol(this, this.ContainingType, node, node.Identifier.GetLocation());
}
else if (node.ExpressionBody != null)
else
{
hasErrors |= this.ValidateDeclarationNameConflictsInScope(localSymbol, diagnostics);
}
var binder = this.GetBinder(node);
// Binder could be null in error scenarios (as above)
BoundBlock block;
if (binder != null)
{
block = blockBinder.BindExpressionBodyAsBlock(node.ExpressionBody, diagnostics);
if (node.Body != null)
{
block = binder.BindBlock(node.Body, diagnostics);
}
else if (node.ExpressionBody != null)
{
block = binder.BindExpressionBodyAsBlock(node.ExpressionBody, diagnostics);
}
else
{
block = null;
hasErrors = true;
// TODO: add a message for this?
diagnostics.Add(ErrorCode.ERR_ConcreteMissingBody, node.Location, localSymbol);
}
}
else
{
block = null;
// TODO: add a message for this?
diagnostics.Add(ErrorCode.ERR_ConcreteMissingBody, node.Location);
hasErrors = true;
}
return new BoundLocalFunctionStatement(node, localSymbol, block, hasErrors);
......
......@@ -2,6 +2,8 @@
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using System.Diagnostics;
using System.Collections.Immutable;
namespace Microsoft.CodeAnalysis.CSharp
{
......@@ -18,6 +20,7 @@ internal sealed class ExecutableCodeBinder : Binder
private readonly CSharpSyntaxNode _root;
private readonly MethodSymbol _owner;
private SmallDictionary<CSharpSyntaxNode, Binder> _lazyBinderMap;
private ImmutableArray<MethodSymbol> _methodSymbolsWithYield;
internal ExecutableCodeBinder(CSharpSyntaxNode root, Symbol memberSymbol, Binder next)
: this(root, memberSymbol, next, next.Flags)
......@@ -45,43 +48,69 @@ internal override Binder GetBinder(CSharpSyntaxNode node)
return this.BinderMap.TryGetValue(node, out binder) ? binder : Next.GetBinder(node);
}
private SmallDictionary<CSharpSyntaxNode, Binder> BinderMap
private void ComputeBinderMap()
{
get
SmallDictionary<CSharpSyntaxNode, Binder> map;
ImmutableArray<MethodSymbol> methodSymbolsWithYield;
var methodSymbol = _owner;
// Ensure that the member symbol is a method symbol.
if ((object)methodSymbol != null && _root != null)
{
if (_lazyBinderMap == null)
var methodsWithYield = ArrayBuilder<CSharpSyntaxNode>.GetInstance();
var symbolsWithYield = ArrayBuilder<MethodSymbol>.GetInstance();
map = LocalBinderFactory.BuildMap(methodSymbol, _root, this, methodsWithYield);
foreach (var methodWithYield in methodsWithYield)
{
SmallDictionary<CSharpSyntaxNode, Binder> map;
var methodSymbol = _owner;
// Ensure that the member symbol is a method symbol.
if ((object)methodSymbol != null && _root != null)
Binder binder;
InMethodBinder inMethod;
if (map.TryGetValue(methodWithYield, out binder) && (inMethod = binder as InMethodBinder) != null)
{
bool sawYield;
map = LocalBinderFactory.BuildMap(methodSymbol, _root, this, out sawYield);
if (sawYield && ((MethodSymbol)this.ContainingMemberOrLambda).MethodKind != MethodKind.AnonymousFunction)
{
for (Binder b = this; b != null; b = b.Next)
{
var inMethod = b as InMethodBinder;
if (inMethod != null)
{
inMethod.MakeIterator();
break;
}
}
}
inMethod.MakeIterator();
symbolsWithYield.Add((MethodSymbol)inMethod.ContainingMemberOrLambda);
}
else
{
map = SmallDictionary<CSharpSyntaxNode, Binder>.Empty;
Debug.Assert(false);
}
}
methodsWithYield.Free();
methodSymbolsWithYield = symbolsWithYield.ToImmutableAndFree();
}
else
{
map = SmallDictionary<CSharpSyntaxNode, Binder>.Empty;
methodSymbolsWithYield = ImmutableArray<MethodSymbol>.Empty;
}
Interlocked.CompareExchange(ref _lazyBinderMap, map, null);
_methodSymbolsWithYield = methodSymbolsWithYield;
}
Interlocked.CompareExchange(ref _lazyBinderMap, map, null);
private SmallDictionary<CSharpSyntaxNode, Binder> BinderMap
{
get
{
if (_lazyBinderMap == null)
{
ComputeBinderMap();
}
return _lazyBinderMap;
}
}
public ImmutableArray<MethodSymbol> MethodSymbolsWithYield
{
get
{
if (_methodSymbolsWithYield.IsDefault)
{
ComputeBinderMap();
}
return _methodSymbolsWithYield;
}
}
}
}
......@@ -25,7 +25,8 @@ internal sealed class LocalBinderFactory : CSharpSyntaxVisitor
{
private readonly SmallDictionary<CSharpSyntaxNode, Binder> _map;
private bool _sawYield;
private readonly MethodSymbol _method;
private readonly ArrayBuilder<CSharpSyntaxNode> _methodsWithYields;
private MethodSymbol _method;
private Binder _enclosing;
private void Visit(CSharpSyntaxNode syntax, Binder enclosing)
......@@ -43,11 +44,17 @@ private void Visit(CSharpSyntaxNode syntax, Binder enclosing)
}
}
public static SmallDictionary<CSharpSyntaxNode, Binder> BuildMap(MethodSymbol method, CSharpSyntaxNode syntax, Binder enclosing, out bool sawYield)
// methodsWithYields will contain all function-declaration-like CSharpSyntaxNodes with yield statements contained within them.
// Currently the types of these are restricted to only be whatever the syntax parameter is, plus any LocalFunctionStatementSyntax contained within it.
// This may change if the language is extended to allow iterator lambdas, in which case the lambda would also be returned.
// (lambdas currently throw a diagnostic in WithLambdaParametersBinder.GetIteratorElementType when a yield is used within them)
public static SmallDictionary<CSharpSyntaxNode, Binder> BuildMap(MethodSymbol method, CSharpSyntaxNode syntax, Binder enclosing, ArrayBuilder<CSharpSyntaxNode> methodsWithYields)
{
var builder = new LocalBinderFactory(method, enclosing);
var builder = new LocalBinderFactory(method, enclosing, methodsWithYields);
builder.Visit(syntax);
sawYield = builder._sawYield;
// the other place this is possible is in a local function
if (builder._sawYield)
methodsWithYields.Add(syntax);
return builder._map;
}
......@@ -62,12 +69,13 @@ public override void VisitCompilationUnit(CompilationUnitSyntax node)
}
}
private LocalBinderFactory(MethodSymbol method, Binder enclosing)
private LocalBinderFactory(MethodSymbol method, Binder enclosing, ArrayBuilder<CSharpSyntaxNode> methodsWithYields)
{
Debug.Assert((object)method != null);
_map = new SmallDictionary<CSharpSyntaxNode, Binder>(ReferenceEqualityComparer.Instance);
_method = method;
_enclosing = enclosing;
_methodsWithYields = methodsWithYields;
}
#region Starting points - these nodes contain statements
......@@ -122,10 +130,39 @@ public override void VisitParenthesizedLambdaExpression(ParenthesizedLambdaExpre
public override void VisitLocalFunctionStatement(LocalFunctionStatementSyntax node)
{
BlockSyntax body = node.Body;
if (body != null)
var body = (CSharpSyntaxNode)node.Body ?? node.ExpressionBody;
MethodSymbol match = null;
// Don't use LookupLocalFunction because it recurses up the tree, as it
// should be defined in the directly enclosing block (see note below)
foreach (var candidate in _enclosing.LocalFunctions)
{
VisitBlock(body);
if (candidate.Name == node.Identifier.Text)
{
match = candidate;
}
}
if (match != null)
{
var oldMethod = _method;
_method = match;
var inMethod = new InMethodBinder(match, _enclosing);
AddToMap(node, inMethod);
if (body != null)
{
Visit(body, inMethod);
}
_method = oldMethod;
}
else
{
// The enclosing block should have found this node and created a LocalFunctionMethodSymbol
// The code that does so is in LocalScopeBinder.BuildLocalFunctions
if (body != null)
{
// do our best to attempt to bind
Visit(body);
}
}
}
......@@ -160,6 +197,10 @@ public override void VisitBlock(BlockSyntax node)
Visit(statement, blockBinder);
if (statement is LocalFunctionStatementSyntax)
{
if (_sawYield)
{
_methodsWithYields.Add(statement);
}
// do not make the parent method an iterator if a local function has a yield
_sawYield = oldSawYield;
}
......
......@@ -354,13 +354,26 @@ protected override void AddLookupSymbolsInfoInSingleBinder(LookupSymbolsInfo res
}
}
}
else if (this.LocalsMap != null && options.CanConsiderLocals())
if (options.CanConsiderLocals())
{
foreach (var local in this.LocalsMap)
if (this.LocalsMap != null)
{
if (originalBinder.CanAddLookupSymbolInfo(local.Value, options, null))
foreach (var local in this.LocalsMap)
{
result.AddSymbol(local.Value, local.Key, 0);
if (originalBinder.CanAddLookupSymbolInfo(local.Value, options, null))
{
result.AddSymbol(local.Value, local.Key, 0);
}
}
}
if (this.LocalFunctionsMap != null)
{
foreach (var local in this.LocalFunctionsMap)
{
if (originalBinder.CanAddLookupSymbolInfo(local.Value, options, null))
{
result.AddSymbol(local.Value, local.Key, 0);
}
}
}
}
......@@ -381,6 +394,7 @@ private bool ReportConflictWithLocal(Symbol local, Symbol newSymbol, string name
diagnostics.Add(ErrorCode.ERR_LocalDuplicate, newLocation, name);
return true;
}
return false;
}
if (newSymbolKind == SymbolKind.Method)
......@@ -391,6 +405,7 @@ private bool ReportConflictWithLocal(Symbol local, Symbol newSymbol, string name
diagnostics.Add(ErrorCode.ERR_LocalDuplicate, newLocation, name);
return true;
}
return false;
}
if (newSymbolKind == SymbolKind.Local || newSymbolKind == SymbolKind.Parameter)
......
......@@ -596,7 +596,7 @@ private void CompileSynthesizedMethods(TypeCompilationState compilationState)
var variableSlotAllocatorOpt = ((object)lambda != null) ?
_moduleBeingBuiltOpt.TryCreateVariableSlotAllocator(lambda, lambda.TopLevelMethod) :
_moduleBeingBuiltOpt.TryCreateVariableSlotAllocator(method, method);
// We make sure that an asynchronous mutation to the diagnostic bag does not
// confuse the method body generator by making a fresh bag and then loading
// any diagnostics emitted into it back into the main diagnostic bag.
......@@ -606,13 +606,21 @@ private void CompileSynthesizedMethods(TypeCompilationState compilationState)
// In case of async lambdas, which synthesize a state machine type during the following rewrite, the containing method has already been uniquely named,
// so there is no need to produce a unique method ordinal for the corresponding state machine type, whose name includes the (unique) containing method name.
const int methodOrdinal = -1;
ArrayBuilder<LambdaDebugInfo> lambdaDebugInfo = ArrayBuilder<LambdaDebugInfo>.GetInstance();
ArrayBuilder<ClosureDebugInfo> closureDebugInfo = ArrayBuilder<ClosureDebugInfo>.GetInstance();
StateMachineTypeSymbol stateMachineType;
// Use full lowering on synthesized methods. Previously this was just an async lowering,
// but with local functions, more lowering types need to be allowed (e.g. iterators)
var loweredBody = LowerBodyOrInitializer(method, methodOrdinal, methodWithBody.Body, null, compilationState, diagnosticsThisMethod, ref variableSlotAllocatorOpt, lambdaDebugInfo, closureDebugInfo, out stateMachineType);
// Previously this was just an async lowering, but local functions can also be iterators, so we need to lower those
IteratorStateMachine iteratorStateMachine;
BoundStatement loweredBody = IteratorRewriter.Rewrite(methodWithBody.Body, method, methodOrdinal, variableSlotAllocatorOpt, compilationState, diagnosticsThisMethod, out iteratorStateMachine);
StateMachineTypeSymbol stateMachine = iteratorStateMachine;
if (!loweredBody.HasErrors)
{
AsyncStateMachine asyncStateMachine;
loweredBody = AsyncRewriter.Rewrite(loweredBody, method, methodOrdinal, variableSlotAllocatorOpt, compilationState, diagnosticsThisMethod, out asyncStateMachine);
Debug.Assert(iteratorStateMachine == null || asyncStateMachine == null);
stateMachine = stateMachine ?? asyncStateMachine;
}
MethodBody emittedBody = null;
if (!diagnosticsThisMethod.HasAnyErrors() && !_globalHasErrors)
{
......@@ -621,9 +629,9 @@ private void CompileSynthesizedMethods(TypeCompilationState compilationState)
method,
methodOrdinal,
loweredBody,
lambdaDebugInfo.ToImmutableAndFree(),
closureDebugInfo.ToImmutableAndFree(),
stateMachineType,
ImmutableArray<LambdaDebugInfo>.Empty,
ImmutableArray<ClosureDebugInfo>.Empty,
stateMachine,
variableSlotAllocatorOpt,
diagnosticsThisMethod,
_debugDocumentProvider,
......@@ -1479,13 +1487,13 @@ private static BoundBlock BindMethodBody(MethodSymbol method, TypeCompilationSta
{
var inMethodBinder = factory.GetBinder(blockSyntax);
Binder binder = new ExecutableCodeBinder(blockSyntax, sourceMethod, inMethodBinder);
var binder = new ExecutableCodeBinder(blockSyntax, sourceMethod, inMethodBinder);
body = binder.BindBlock(blockSyntax, diagnostics);
importChain = binder.ImportChain;
if (inMethodBinder.IsDirectlyInIterator)
foreach (var iterator in binder.MethodSymbolsWithYield)
{
foreach (var parameter in method.Parameters)
foreach (var parameter in iterator.Parameters)
{
if (parameter.RefKind != RefKind.None)
{
......@@ -1497,15 +1505,19 @@ private static BoundBlock BindMethodBody(MethodSymbol method, TypeCompilationSta
}
}
if (sourceMethod.IsUnsafe && compilation.Options.AllowUnsafe) // Don't cascade
var sourceIterator = iterator as SourceMethodSymbol;
if (sourceIterator != null)
{
diagnostics.Add(ErrorCode.ERR_IllegalInnerUnsafe, sourceMethod.Locations[0]);
}
if (sourceIterator.IsUnsafe && compilation.Options.AllowUnsafe) // Don't cascade
{
diagnostics.Add(ErrorCode.ERR_IllegalInnerUnsafe, sourceIterator.Locations[0]);
}
if (sourceMethod.IsVararg)
{
// error CS1636: __arglist is not allowed in the parameter list of iterators
diagnostics.Add(ErrorCode.ERR_VarargsIterator, sourceMethod.Locations[0]);
if (sourceIterator.IsVararg)
{
// error CS1636: __arglist is not allowed in the parameter list of iterators
diagnostics.Add(ErrorCode.ERR_VarargsIterator, sourceIterator.Locations[0]);
}
}
}
}
......
......@@ -2043,7 +2043,8 @@ public override BoundNode VisitSequence(BoundSequence node)
public override BoundNode VisitSequencePoint(BoundSequencePoint node)
{
VisitStatement(node.StatementOpt);
if (node.StatementOpt != null)
VisitStatement(node.StatementOpt);
return null;
}
......@@ -2055,7 +2056,8 @@ public override BoundNode VisitSequencePointExpression(BoundSequencePointExpress
public override BoundNode VisitSequencePointWithSpan(BoundSequencePointWithSpan node)
{
VisitStatement(node.StatementOpt);
if (node.StatementOpt != null)
VisitStatement(node.StatementOpt);
return null;
}
......
......@@ -46,12 +46,19 @@ private bool ImplicitReturnIsOkay(MethodSymbol method)
public override BoundNode VisitLocalFunctionStatement(BoundLocalFunctionStatement node)
{
var topLevelDebugId = new DebugId(_methodOrdinal, _compilationState.ModuleBuilderOpt.CurrentGenerationOrdinal);
var localFunctionDebugId = new DebugId(-1, _compilationState.ModuleBuilderOpt.CurrentGenerationOrdinal);
var synth = new SynthesizedLocalFunction(_method.ContainingType, _method, topLevelDebugId, node, localFunctionDebugId);
_loweringTranslation.Add(node.LocalSymbol, synth);
var translatedBody = (BoundStatement)base.Visit(node.Body);
// Have to do ControlFlowPass here because in MethodCompiler, we don't call this for synthed methods
// rather we go directly to LowerBodyOrInitializer, which skips over flow analysis (which is in CompileMethod)
// (the same thing - calling ControlFlowPass.Analyze in the lowering - is done for lambdas)
var endIsReachable = ControlFlowPass.Analyze(node.LocalSymbol.DeclaringCompilation, node.LocalSymbol, node, _diagnostics);
var endIsReachable = ControlFlowPass.Analyze(node.LocalSymbol.DeclaringCompilation, node.LocalSymbol, translatedBody, _diagnostics);
var flowAnalyzed = node.Body;
var flowAnalyzed = translatedBody;
if (endIsReachable)
{
if (ImplicitReturnIsOkay(node.LocalSymbol))
......@@ -64,14 +71,9 @@ public override BoundNode VisitLocalFunctionStatement(BoundLocalFunctionStatemen
}
}
var topLevelDebugId = new DebugId(_methodOrdinal, _compilationState.ModuleBuilderOpt.CurrentGenerationOrdinal);
var localFunctionDebugId = new DebugId(-1, _compilationState.ModuleBuilderOpt.CurrentGenerationOrdinal);
var synth = new SynthesizedLocalFunction(_method.ContainingType, _method, topLevelDebugId, node, localFunctionDebugId);
_compilationState.ModuleBuilderOpt.AddSynthesizedDefinition(_method.ContainingType, synth);
_compilationState.AddSynthesizedMethod(synth, flowAnalyzed);
_loweringTranslation.Add(node.LocalSymbol, synth);
return new BoundNoOpStatement(node.Syntax, NoOpStatementFlavor.Default);
}
......@@ -104,7 +106,8 @@ internal sealed class SynthesizedLocalFunction : SynthesizedMethodBaseSymbol, IS
localFunction.SyntaxTree.GetReference(localFunction.Body.Syntax),
localFunction.Syntax.GetLocation(),
MakeName(topLevelMethod.Name, topLevelMethodId, localFunction.LocalSymbol.Name, localFunctionId),
DeclarationModifiers.Private | DeclarationModifiers.Static)
DeclarationModifiers.Private | DeclarationModifiers.Static |
(localFunction.LocalSymbol.IsAsync ? DeclarationModifiers.Async : 0))
{
_topLevelMethod = topLevelMethod;
......
......@@ -127,6 +127,11 @@ private PEModuleBuilder EmitModule
private BoundStatement AddSequencePoint(BoundStatement node)
{
if (node == null)
{
return node;
}
if (this.GenerateDebugInfo && !node.WasCompilerGenerated)
{
node = new BoundSequencePoint(node.Syntax, node);
......
......@@ -7986,6 +7986,8 @@ private static bool IsDeclarationModifier(SyntaxKind kind)
private bool ParseLocalFunctionStatement(out LocalFunctionStatementSyntax decl)
{
ResetPoint resetPoint = this.GetResetPoint();
var modifiers = _pool.Allocate();
this.ParseModifiers(modifiers);
TypeSyntax type = this.ParseReturnType();
SyntaxToken identifier = this.EatToken(SyntaxKind.IdentifierToken);
if (this.CurrentToken.Kind != SyntaxKind.OpenParenToken)
......@@ -7996,6 +7998,22 @@ private bool ParseLocalFunctionStatement(out LocalFunctionStatementSyntax decl)
return false;
}
this.Release(ref resetPoint);
bool parentScopeIsInAsync = IsInAsync;
IsInAsync = false;
for (int i = 0; i < modifiers.Count; i++)
{
switch (modifiers[i].Kind)
{
case SyntaxKind.AsyncKeyword:
IsInAsync = true;
break;
default:
modifiers[i] = this.AddError(modifiers[i], ErrorCode.ERR_BadMemberFlag, ((SyntaxToken)modifiers[i]).Text);
break;
}
}
ParameterListSyntax paramList = this.ParseParenthesizedParameterList(allowThisKeyword: true, allowDefaults: true, allowAttributes: true);
BlockSyntax blockBody;
......@@ -8004,14 +8022,16 @@ private bool ParseLocalFunctionStatement(out LocalFunctionStatementSyntax decl)
this.ParseBlockAndExpressionBodiesWithSemicolon(out blockBody, out expressionBody, out semicolon);
decl = _syntaxFactory.LocalFunctionStatement(
type,
identifier,
paramList,
blockBody,
expressionBody,
semicolon);
modifiers.ToTokenList(),
type,
identifier,
paramList,
blockBody,
expressionBody,
semicolon);
decl = CheckForBlockAndExpressionBody(blockBody, expressionBody, decl);
IsInAsync = parentScopeIsInAsync;
return true;
}
......
......@@ -1173,17 +1173,20 @@ Microsoft.CodeAnalysis.CSharp.Syntax.LocalDeclarationStatementSyntax.WithModifie
Microsoft.CodeAnalysis.CSharp.Syntax.LocalDeclarationStatementSyntax.WithSemicolonToken(Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalDeclarationStatementSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.AddBodyStatements(params Microsoft.CodeAnalysis.CSharp.Syntax.StatementSyntax[] items) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.AddModifiers(params Microsoft.CodeAnalysis.SyntaxToken[] items) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.AddParameterListParameters(params Microsoft.CodeAnalysis.CSharp.Syntax.ParameterSyntax[] items) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.Body.get -> Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.ExpressionBody.get -> Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.Identifier.get -> Microsoft.CodeAnalysis.SyntaxToken
Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.Modifiers.get -> Microsoft.CodeAnalysis.SyntaxTokenList
Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.ParameterList.get -> Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.ReturnType.get -> Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.SemicolonToken.get -> Microsoft.CodeAnalysis.SyntaxToken
Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.Update(Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax returnType, Microsoft.CodeAnalysis.SyntaxToken identifier, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax parameterList, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax body, Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax expressionBody, Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.Update(Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax returnType, Microsoft.CodeAnalysis.SyntaxToken identifier, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax parameterList, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax body, Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax expressionBody, Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.WithBody(Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax body) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.WithExpressionBody(Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax expressionBody) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.WithIdentifier(Microsoft.CodeAnalysis.SyntaxToken identifier) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.WithModifiers(Microsoft.CodeAnalysis.SyntaxTokenList modifiers) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.WithParameterList(Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax parameterList) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.WithReturnType(Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax returnType) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.WithSemicolonToken(Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax
......@@ -3743,9 +3746,9 @@ static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.LocalDeclarationStatement(Mic
static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.LocalDeclarationStatement(Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeclarationSyntax declaration) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalDeclarationStatementSyntax
static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.LocalDeclarationStatement(Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeclarationSyntax declaration, Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalDeclarationStatementSyntax
static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.LocalFunctionStatement(Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax returnType, Microsoft.CodeAnalysis.SyntaxToken identifier) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax
static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.LocalFunctionStatement(Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax returnType, Microsoft.CodeAnalysis.SyntaxToken identifier, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax parameterList, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax body, Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax expressionBody) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax
static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.LocalFunctionStatement(Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax returnType, Microsoft.CodeAnalysis.SyntaxToken identifier, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax parameterList, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax body, Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax expressionBody, Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax
static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.LocalFunctionStatement(Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax returnType, string identifier) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax
static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.LocalFunctionStatement(Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax returnType, Microsoft.CodeAnalysis.SyntaxToken identifier, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax parameterList, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax body, Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax expressionBody) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax
static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.LocalFunctionStatement(Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax returnType, Microsoft.CodeAnalysis.SyntaxToken identifier, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax parameterList, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax body, Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax expressionBody, Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax
static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.LockStatement(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expression, Microsoft.CodeAnalysis.CSharp.Syntax.StatementSyntax statement) -> Microsoft.CodeAnalysis.CSharp.Syntax.LockStatementSyntax
static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.LockStatement(Microsoft.CodeAnalysis.SyntaxToken lockKeyword, Microsoft.CodeAnalysis.SyntaxToken openParenToken, Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expression, Microsoft.CodeAnalysis.SyntaxToken closeParenToken, Microsoft.CodeAnalysis.CSharp.Syntax.StatementSyntax statement) -> Microsoft.CodeAnalysis.CSharp.Syntax.LockStatementSyntax
static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.MakeRefExpression(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expression) -> Microsoft.CodeAnalysis.CSharp.Syntax.MakeRefExpressionSyntax
......
......@@ -35,7 +35,7 @@ internal class LocalFunctionMethodSymbol : SourceMethodSymbol
this.MakeFlags(
MethodKind.LocalFunction,
DeclarationModifiers.Static, // TODO: Will change when we allow local captures (also change in LocalFunctionRewriter)
DeclarationModifiers.Static | syntax.Modifiers.ToDeclarationModifiers(), // TODO: Will change when we allow local captures (also change in LocalFunctionRewriter)
returnsVoid: false, // will be fixed in MethodChecks
isExtensionMethod: isExtensionMethod);
}
......
......@@ -1635,6 +1635,7 @@
</Node>
<Node Name="LocalFunctionStatementSyntax" Base="StatementSyntax">
<Kind Name="LocalFunctionStatement"/>
<Field Name="Modifiers" Type="SyntaxList&lt;SyntaxToken&gt;"/>
<Field Name="ReturnType" Type="TypeSyntax"/>
<Field Name="Identifier" Type="SyntaxToken">
<PropertyComment>
......
// 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;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
using System.Threading;
using System.Linq;
namespace Microsoft.CodeAnalysis.CSharp.UnitTests
{
public class LocalFunctionTests : CSharpTestBase
{
[Fact]
public void LocalFunctionEndToEnd()
public void EndToEnd()
{
var source = @"
using System;
......@@ -38,7 +31,89 @@ void Local()
}
[Fact]
public void LocalFunctionEnumerator()
public void ExpressionBody()
{
var source = @"
using System;
class Program
{
static void Main(string[] args)
{
int Local() => 2;
Console.WriteLine(Local());
}
}
";
var comp = CompileAndVerify(source, expectedOutput: @"
2
");
}
[Fact]
public void Recursion()
{
var source = @"
using System;
using System.Collections.Generic;
class Program
{
static void Main(string[] args)
{
void Foo(int depth)
{
if (depth > 10)
{
Console.WriteLine(2);
return;
}
Foo(depth + 1);
}
Foo(0);
}
}
";
var comp = CompileAndVerify(source, expectedOutput: @"
2
");
}
[Fact]
public void MutualRecursion()
{
var source = @"
using System;
using System.Collections.Generic;
class Program
{
static void Main(string[] args)
{
void Foo(int depth)
{
if (depth > 10)
{
Console.WriteLine(2);
return;
}
void Bar(int depth2)
{
Foo(depth2 + 1);
}
Bar(depth + 1);
}
Foo(0);
}
}
";
var comp = CompileAndVerify(source, expectedOutput: @"
2
");
}
[Fact]
public void Enumerator()
{
var source = @"
using System;
......@@ -60,5 +135,146 @@ IEnumerable<int> Local()
2
");
}
[Fact]
public void Async()
{
var source = @"
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
class Program
{
static void Main(string[] args)
{
async Task<int> Local()
{
return await Task.FromResult(2);
}
Console.WriteLine(Local().Result);
}
}
";
var compilation = CreateCompilationWithMscorlib45(source, options: new CSharpCompilationOptions(OutputKind.ConsoleApplication));
var comp = CompileAndVerify(compilation, expectedOutput: @"
2
");
}
[Fact]
public void NoBody()
{
var source = @"
using System;
using System.Collections.Generic;
class Program
{
static void Main(string[] args)
{
void Local1()
void Local2();
}
}
";
var option = TestOptions.ReleaseExe.WithWarningLevel(0);
CreateCompilationWithMscorlibAndSystemCore(source, options: option).VerifyDiagnostics(
// (9,22): error CS1002: ; expected
// void Local1()
Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(9, 22),
// (9,9): error CS0501: 'Program.Local1()' must declare a body because it is not marked abstract, extern, or partial
// void Local1()
Diagnostic(ErrorCode.ERR_ConcreteMissingBody, @"void Local1()
").WithArguments("Program.Local1()").WithLocation(9, 9),
// (10,9): error CS0501: 'Program.Local2()' must declare a body because it is not marked abstract, extern, or partial
// void Local2();
Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "void Local2();").WithArguments("Program.Local2()").WithLocation(10, 9)
);
}
[Fact]
public void ByRefIterator()
{
var source = @"
using System;
using System.Collections.Generic;
class Program
{
static void Main(string[] args)
{
IEnumerable<int> Local(ref int x)
{
yield return x;
}
int y = 2;
Console.WriteLine(string.Join("","", Local(ref y)));
}
}
";
var option = TestOptions.ReleaseExe.WithWarningLevel(0);
CreateCompilationWithMscorlibAndSystemCore(source, options: option).VerifyDiagnostics(
// (9,40): error CS1623: Iterators cannot have ref or out parameters
// IEnumerable<int> Local(ref int x)
Diagnostic(ErrorCode.ERR_BadIteratorArgType, "x").WithLocation(9, 40)
);
}
[Fact]
public void ArglistIterator()
{
var source = @"
using System;
using System.Collections.Generic;
class Program
{
static void Main(string[] args)
{
IEnumerable<int> Local(__arglist)
{
yield return 2;
}
Console.WriteLine(string.Join("","", Local(__arglist())));
}
}
";
var option = TestOptions.ReleaseExe.WithWarningLevel(0);
CreateCompilationWithMscorlibAndSystemCore(source, options: option).VerifyDiagnostics(
// (9,26): error CS1636: __arglist is not allowed in the parameter list of iterators
// IEnumerable<int> Local(__arglist)
Diagnostic(ErrorCode.ERR_VarargsIterator, "Local").WithLocation(9, 26)
);
}
[Fact]
public void PartialExpressionBody()
{
var source = @"
using System;
using System.Collections.Generic;
class Program
{
static void Main(string[] args)
{
void Local() => Console.Writ
}
}
";
var option = TestOptions.ReleaseExe.WithWarningLevel(0);
CreateCompilationWithMscorlibAndSystemCore(source, options: option).VerifyDiagnostics(
// (9,37): error CS1002: ; expected
// void Local() => Console.Writ
Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(9, 37),
// (9,33): error CS0117: 'Console' does not contain a definition for 'Writ'
// void Local() => Console.Writ
Diagnostic(ErrorCode.ERR_NoSuchMember, "Writ").WithArguments("System.Console", "Writ").WithLocation(9, 33),
// (9,25): error CS0201: Only assignment, call, increment, decrement, and new object expressions can be used as a statement
// void Local() => Console.Writ
Diagnostic(ErrorCode.ERR_IllegalStatement, "Console.Writ").WithLocation(9, 25)
);
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册