// An anonymous function can be of the form: // // delegate { } (missing parameter list) // delegate (int x) { } (typed parameter list) // x => ... (type-inferred parameter list) // (x) => ... (type-inferred parameter list) // (x, y) => ... (type-inferred parameter list) // ( ) => ... (typed parameter list) // (ref int x) => ... (typed parameter list) // (int x, out int y) => ... (typed parameter list) // // and so on. We want to canonicalize these various ways of writing the signatures. // // If we are in the first case then the name, modifier and type arrays are all null. // If we have a parameter list then the names array is non-null, but possibly empty. // If we have types then the types array is non-null, but possibly empty. // If we have no modifiers then the modifiers array is null; if we have any modifiers // then the modifiers array is non-null and not empty. private UnboundLambda AnalyzeAnonymousFunction( AnonymousFunctionExpressionSyntax syntax, BindingDiagnosticBag diagnostics) { Debug.Assert(syntax != null); Debug.Assert(syntax.IsAnonymousFunction()); ImmutableArray <string> names = default; ImmutableArray <RefKind> refKinds = default; ImmutableArray <DeclarationScope> scopes = default; ImmutableArray <TypeWithAnnotations> types = default; RefKind returnRefKind = RefKind.None; TypeWithAnnotations returnType = default; ImmutableArray <SyntaxList <AttributeListSyntax> > parameterAttributes = default; var namesBuilder = ArrayBuilder <string> .GetInstance(); ImmutableArray <bool> discardsOpt = default; SeparatedSyntaxList <ParameterSyntax>?parameterSyntaxList = null; bool hasSignature; if (syntax is LambdaExpressionSyntax lambdaSyntax) { checkAttributes(syntax, lambdaSyntax.AttributeLists, diagnostics); } switch (syntax.Kind()) { default: case SyntaxKind.SimpleLambdaExpression: // x => ... hasSignature = true; var simple = (SimpleLambdaExpressionSyntax)syntax; namesBuilder.Add(simple.Parameter.Identifier.ValueText); break; case SyntaxKind.ParenthesizedLambdaExpression: // (T x, U y) => ... // (x, y) => ... hasSignature = true; var paren = (ParenthesizedLambdaExpressionSyntax)syntax; if (paren.ReturnType is { } returnTypeSyntax) { (returnRefKind, returnType) = BindExplicitLambdaReturnType(returnTypeSyntax, diagnostics); } parameterSyntaxList = paren.ParameterList.Parameters; CheckParenthesizedLambdaParameters(parameterSyntaxList.Value, diagnostics); break; case SyntaxKind.AnonymousMethodExpression: // delegate (int x) { } // delegate { } var anon = (AnonymousMethodExpressionSyntax)syntax; hasSignature = anon.ParameterList != null; if (hasSignature) { parameterSyntaxList = anon.ParameterList !.Parameters; } break; } var isAsync = syntax.Modifiers.Any(SyntaxKind.AsyncKeyword); var isStatic = syntax.Modifiers.Any(SyntaxKind.StaticKeyword); if (parameterSyntaxList != null) { var hasExplicitlyTypedParameterList = true; var typesBuilder = ArrayBuilder <TypeWithAnnotations> .GetInstance(); var refKindsBuilder = ArrayBuilder <RefKind> .GetInstance(); var scopesBuilder = ArrayBuilder <DeclarationScope> .GetInstance(); var attributesBuilder = ArrayBuilder <SyntaxList <AttributeListSyntax> > .GetInstance(); // In the batch compiler case we probably should have given a syntax error if the // user did something like (int x, y)=>x+y -- but in the IDE scenario we might be in // this case. If we are, then rather than try to make partial deductions from the // typed formal parameters, simply bail out and treat it as an untyped lambda. // // However, we still want to give errors on every bad type in the list, even if one // is missing. int underscoresCount = 0; foreach (var p in parameterSyntaxList.Value) { if (p.Identifier.IsUnderscoreToken()) { underscoresCount++; } checkAttributes(syntax, p.AttributeLists, diagnostics); if (p.Default != null) { Error(diagnostics, ErrorCode.ERR_DefaultValueNotAllowed, p.Default.EqualsToken); } if (p.IsArgList) { Error(diagnostics, ErrorCode.ERR_IllegalVarArgs, p); continue; } var typeSyntax = p.Type; TypeWithAnnotations type = default; var refKind = RefKind.None; var scope = DeclarationScope.Unscoped; if (typeSyntax == null) { hasExplicitlyTypedParameterList = false; } else { type = BindType(typeSyntax, diagnostics); ParameterHelpers.CheckParameterModifiers(p, diagnostics, parsingFunctionPointerParams: false, parsingLambdaParams: true); refKind = ParameterHelpers.GetModifiers(p.Modifiers, out _, out _, out _, out scope); } namesBuilder.Add(p.Identifier.ValueText); typesBuilder.Add(type); refKindsBuilder.Add(refKind); scopesBuilder.Add(scope); attributesBuilder.Add(syntax.Kind() == SyntaxKind.ParenthesizedLambdaExpression ? p.AttributeLists : default);
// An anonymous function can be of the form: // // delegate { } (missing parameter list) // delegate (int x) { } (typed parameter list) // x => ... (type-inferred parameter list) // (x) => ... (type-inferred parameter list) // (x, y) => ... (type-inferred parameter list) // ( ) => ... (typed parameter list) // (ref int x) => ... (typed parameter list) // (int x, out int y) => ... (typed parameter list) // // and so on. We want to canonicalize these various ways of writing the signatures. // // If we are in the first case then the name, modifier and type arrays are all null. // If we have a parameter list then the names array is non-null, but possibly empty. // If we have types then the types array is non-null, but possibly empty. // If we have no modifiers then the modifiers array is null; if we have any modifiers // then the modifiers array is non-null and not empty. private UnboundLambda AnalyzeAnonymousFunction( AnonymousFunctionExpressionSyntax syntax, BindingDiagnosticBag diagnostics) { Debug.Assert(syntax != null); Debug.Assert(syntax.IsAnonymousFunction()); var names = default(ImmutableArray <string>); var refKinds = default(ImmutableArray <RefKind>); var types = default(ImmutableArray <TypeWithAnnotations>); var namesBuilder = ArrayBuilder <string> .GetInstance(); ImmutableArray <bool> discardsOpt = default; SeparatedSyntaxList <ParameterSyntax>?parameterSyntaxList = null; bool hasSignature; switch (syntax.Kind()) { default: case SyntaxKind.SimpleLambdaExpression: // x => ... hasSignature = true; var simple = (SimpleLambdaExpressionSyntax)syntax; namesBuilder.Add(simple.Parameter.Identifier.ValueText); break; case SyntaxKind.ParenthesizedLambdaExpression: // (T x, U y) => ... // (x, y) => ... hasSignature = true; var paren = (ParenthesizedLambdaExpressionSyntax)syntax; parameterSyntaxList = paren.ParameterList.Parameters; CheckParenthesizedLambdaParameters(parameterSyntaxList.Value, diagnostics); break; case SyntaxKind.AnonymousMethodExpression: // delegate (int x) { } // delegate { } var anon = (AnonymousMethodExpressionSyntax)syntax; hasSignature = anon.ParameterList != null; if (hasSignature) { parameterSyntaxList = anon.ParameterList !.Parameters; } break; } var isAsync = syntax.Modifiers.Any(SyntaxKind.AsyncKeyword); var isStatic = syntax.Modifiers.Any(SyntaxKind.StaticKeyword); if (parameterSyntaxList != null) { var hasExplicitlyTypedParameterList = true; var allValue = true; var typesBuilder = ArrayBuilder <TypeWithAnnotations> .GetInstance(); var refKindsBuilder = ArrayBuilder <RefKind> .GetInstance(); // In the batch compiler case we probably should have given a syntax error if the // user did something like (int x, y)=>x+y -- but in the IDE scenario we might be in // this case. If we are, then rather than try to make partial deductions from the // typed formal parameters, simply bail out and treat it as an untyped lambda. // // However, we still want to give errors on every bad type in the list, even if one // is missing. int underscoresCount = 0; foreach (var p in parameterSyntaxList.Value) { if (p.Identifier.IsUnderscoreToken()) { underscoresCount++; } foreach (var attributeList in p.AttributeLists) { Error(diagnostics, ErrorCode.ERR_AttributesNotAllowed, attributeList); } if (p.Default != null) { Error(diagnostics, ErrorCode.ERR_DefaultValueNotAllowed, p.Default.EqualsToken); } if (p.IsArgList) { Error(diagnostics, ErrorCode.ERR_IllegalVarArgs, p); continue; } var typeSyntax = p.Type; TypeWithAnnotations type = default; var refKind = RefKind.None; if (typeSyntax == null) { hasExplicitlyTypedParameterList = false; } else { type = BindType(typeSyntax, diagnostics); foreach (var modifier in p.Modifiers) { switch (modifier.Kind()) { case SyntaxKind.RefKeyword: refKind = RefKind.Ref; allValue = false; break; case SyntaxKind.OutKeyword: refKind = RefKind.Out; allValue = false; break; case SyntaxKind.InKeyword: refKind = RefKind.In; allValue = false; break; case SyntaxKind.ParamsKeyword: // This was a parse error in the native compiler; // it is a semantic analysis error in Roslyn. See comments to // changeset 1674 for details. Error(diagnostics, ErrorCode.ERR_IllegalParams, p); break; case SyntaxKind.ThisKeyword: Error(diagnostics, ErrorCode.ERR_ThisInBadContext, modifier); break; } } } namesBuilder.Add(p.Identifier.ValueText); typesBuilder.Add(type); refKindsBuilder.Add(refKind); } discardsOpt = computeDiscards(parameterSyntaxList.Value, underscoresCount); if (hasExplicitlyTypedParameterList) { types = typesBuilder.ToImmutable(); } if (!allValue) { refKinds = refKindsBuilder.ToImmutable(); } typesBuilder.Free(); refKindsBuilder.Free(); } if (hasSignature) { names = namesBuilder.ToImmutable(); } namesBuilder.Free(); return(new UnboundLambda(syntax, this, diagnostics.AccumulatesDependencies, refKinds, types, names, discardsOpt, isAsync, isStatic));