diff --git a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md index f9f737e36085d..01fd5dd0c7d69 100644 --- a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md +++ b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md @@ -2,6 +2,31 @@ This document lists known breaking changes in Roslyn after .NET 9 general release (.NET SDK version 9.0.100) through .NET 10 general release (.NET SDK version 10.0.100). +## `scoped` in a lambda parameter list is now always a modifier. + +***Introduced in Visual Studio 2022 version 17.13*** + +C# 14 introduces the ability to write a lambda with parameter modifiers, without having to specify a parameter type: +https://github.com/dotnet/csharplang/blob/main/proposals/simple-lambda-parameters-with-modifiers.md + +As part of this work, a breaking change was accepted where `scoped` will always be treated as a modifier +in a lambda parameter, even where it might have been accepted as a type name in the past. For example: + +```c# +var v = (scoped scoped s) => { ... }; + +ref struct @scoped { } +``` + +In C# 14 this will be an error as both `scoped` tokens are treated as modifiers. The workaround is to +use `@` in the type name position like so: + +```c# +var v = (scoped @scoped s) => { ... }; + +ref struct @scoped { } +``` + ## `Span` and `ReadOnlySpan` overloads are applicable in more scenarios in C# 14 and newer ***Introduced in Visual Studio 2022 version 17.13*** diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs index fd65a700503d9..fcd318d4479b4 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs @@ -3,9 +3,9 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; +using System.Linq; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; @@ -20,6 +20,7 @@ internal partial class Binder // delegate (int x) { } (typed parameter list) // x => ... (type-inferred parameter list) // (x) => ... (type-inferred parameter list) + // (ref x) => ... (type-inferred parameter list with modifiers) // (x, y) => ... (type-inferred parameter list) // ( ) => ... (typed parameter list) // (ref int x) => ... (typed parameter list) @@ -52,7 +53,7 @@ private UnboundLambda AnalyzeAnonymousFunction( var namesBuilder = ArrayBuilder.GetInstance(); ImmutableArray discardsOpt = default; - SeparatedSyntaxList? parameterSyntaxList = null; + SeparatedSyntaxList? parameterSyntaxListOpt = null; bool hasSignature; if (syntax is LambdaExpressionSyntax lambdaSyntax) @@ -80,8 +81,9 @@ private UnboundLambda AnalyzeAnonymousFunction( { (returnRefKind, returnType) = BindExplicitLambdaReturnType(returnTypeSyntax, diagnostics); } - parameterSyntaxList = paren.ParameterList.Parameters; - CheckParenthesizedLambdaParameters(parameterSyntaxList.Value, diagnostics); + + parameterSyntaxListOpt = paren.ParameterList.Parameters; + CheckParenthesizedLambdaParameters(parameterSyntaxListOpt.Value, diagnostics); break; case SyntaxKind.AnonymousMethodExpression: // delegate (int x) { } @@ -92,7 +94,7 @@ private UnboundLambda AnalyzeAnonymousFunction( hasSignature = anon.ParameterList != null; if (hasSignature) { - parameterSyntaxList = anon.ParameterList!.Parameters; + parameterSyntaxListOpt = anon.ParameterList!.Parameters; } break; @@ -115,9 +117,10 @@ private UnboundLambda AnalyzeAnonymousFunction( } } - if (parameterSyntaxList != null) + if (parameterSyntaxListOpt is { } parameterSyntaxList) { - var hasExplicitlyTypedParameterList = true; + var isAnonymousMethod = syntax.IsKind(SyntaxKind.AnonymousMethodExpression); + var hasExplicitlyTypedParameterList = parameterSyntaxList.All(static p => p.Type != null); var typesBuilder = ArrayBuilder.GetInstance(); var refKindsBuilder = ArrayBuilder.GetInstance(); @@ -135,10 +138,12 @@ private UnboundLambda AnalyzeAnonymousFunction( int parameterCount = 0; int underscoresCount = 0; - foreach (var p in parameterSyntaxList.Value) + int firstDefault = -1; + for (int i = 0, n = parameterSyntaxList.Count; i < n; i++) { parameterCount++; + var p = parameterSyntaxList[i]; if (p.Identifier.IsUnderscoreToken()) { underscoresCount++; @@ -146,9 +151,13 @@ private UnboundLambda AnalyzeAnonymousFunction( checkAttributes(syntax, p.AttributeLists, diagnostics); - var isAnonymousMethod = syntax.IsKind(SyntaxKind.AnonymousMethodExpression); if (p.Default != null) { + if (firstDefault == -1) + { + firstDefault = i; + } + if (isAnonymousMethod) { Error(diagnostics, ErrorCode.ERR_DefaultValueNotAllowed, p.Default.EqualsToken); @@ -165,43 +174,43 @@ private UnboundLambda AnalyzeAnonymousFunction( continue; } - var typeSyntax = p.Type; - TypeWithAnnotations type = default; - var refKind = RefKind.None; - var scope = ScopedKind.None; + var typeOpt = p.Type is not null ? BindType(p.Type, diagnostics) : default; - if (typeSyntax == null) - { - hasExplicitlyTypedParameterList = false; - } - else + var refKind = ParameterHelpers.GetModifiers(p.Modifiers, out _, out var paramsKeyword, out _, out var scope); + var isParams = paramsKeyword.Kind() != SyntaxKind.None; + + ParameterHelpers.CheckParameterModifiers(p, diagnostics, parsingFunctionPointerParams: false, + parsingLambdaParams: !isAnonymousMethod, + parsingAnonymousMethodParams: isAnonymousMethod); + + ParameterHelpers.ReportParameterErrors( + owner: null, p, ordinal: i, lastParameterIndex: n - 1, isParams: isParams, typeOpt, + refKind, containingSymbol: null, thisKeyword: default, paramsKeyword: paramsKeyword, firstDefault, diagnostics); + + if (parameterCount == parameterSyntaxList.Count && + paramsKeyword.Kind() != SyntaxKind.None && + !typeOpt.IsDefault && + typeOpt.IsSZArray()) { - type = BindType(typeSyntax, diagnostics); - ParameterHelpers.CheckParameterModifiers(p, diagnostics, parsingFunctionPointerParams: false, - parsingLambdaParams: !isAnonymousMethod, - parsingAnonymousMethodParams: isAnonymousMethod); - refKind = ParameterHelpers.GetModifiers(p.Modifiers, out _, out var paramsKeyword, out _, out scope); - - var isLastParameter = parameterCount == parameterSyntaxList.Value.Count; - if (isLastParameter && paramsKeyword.Kind() != SyntaxKind.None && type.IsSZArray()) - { - ReportUseSiteDiagnosticForSynthesizedAttribute(Compilation, - WellKnownMember.System_ParamArrayAttribute__ctor, - diagnostics, - paramsKeyword.GetLocation()); - } + ReportUseSiteDiagnosticForSynthesizedAttribute(Compilation, + WellKnownMember.System_ParamArrayAttribute__ctor, + diagnostics, + paramsKeyword.GetLocation()); } namesBuilder.Add(p.Identifier.ValueText); - typesBuilder.Add(type); + typesBuilder.Add(typeOpt); refKindsBuilder.Add(refKind); scopesBuilder.Add(scope); attributesBuilder.Add(syntax.Kind() == SyntaxKind.ParenthesizedLambdaExpression ? p.AttributeLists : default); defaultValueBuilder.Add(p.Default); } - discardsOpt = computeDiscards(parameterSyntaxList.Value, underscoresCount); + discardsOpt = computeDiscards(parameterSyntaxListOpt.Value, underscoresCount); + // Only include the types if *all* the parameters had types. Otherwise, if there were no parameter + // types (or a mix of typed and untyped parameters) include no types. Note, in the latter case we will + // have already reported an error about this issue. if (hasExplicitlyTypedParameterList) { types = typesBuilder.ToImmutable(); @@ -241,7 +250,7 @@ private UnboundLambda AnalyzeAnonymousFunction( namesBuilder.Free(); - return UnboundLambda.Create(syntax, this, diagnostics.AccumulatesDependencies, returnRefKind, returnType, parameterAttributes, refKinds, scopes, types, names, discardsOpt, parameterSyntaxList, defaultValues, isAsync: isAsync, isStatic: isStatic); + return UnboundLambda.Create(syntax, this, diagnostics.AccumulatesDependencies, returnRefKind, returnType, parameterAttributes, refKinds, scopes, types, names, discardsOpt, parameterSyntaxListOpt, defaultValues, isAsync: isAsync, isStatic: isStatic); static ImmutableArray computeDiscards(SeparatedSyntaxList parameters, int underscoresCount) { @@ -308,39 +317,40 @@ private static void CheckParenthesizedLambdaParameters( { if (parameterSyntaxList.Count > 0) { - var hasTypes = parameterSyntaxList[0].Type != null; + // If one parameter has a type, then all parameters must have a type. + var requiresTypes = parameterSyntaxList.Any(static p => p.Type != null); - checkForImplicitDefault(hasTypes, parameterSyntaxList[0], diagnostics); - - for (int i = 1, n = parameterSyntaxList.Count; i < n; i++) + foreach (var parameter in parameterSyntaxList) { - var parameter = parameterSyntaxList[i]; - // Ignore parameters with missing names. We'll have already reported an error // about them in the parser. if (!parameter.Identifier.IsMissing) { - var thisParameterHasType = parameter.Type != null; - - if (hasTypes != thisParameterHasType) + if (requiresTypes) { - diagnostics.Add(ErrorCode.ERR_InconsistentLambdaParameterUsage, - parameter.Type?.GetLocation() ?? parameter.Identifier.GetLocation()); + if (parameter.Type is null) + { + diagnostics.Add(ErrorCode.ERR_InconsistentLambdaParameterUsage, + parameter.Identifier.GetLocation()); + } + } + else + { + if (parameter.Default != null) + { + diagnostics.Add(ErrorCode.ERR_ImplicitlyTypedDefaultParameter, + parameter.Identifier.GetLocation(), parameter.Identifier.Text); + } } - checkForImplicitDefault(thisParameterHasType, parameter, diagnostics); + // Check if `(ref i) => ...` is supported by this language version. + if (parameter.Modifiers.Count > 0 && parameter.Type is null) + { + CheckFeatureAvailability(parameter, MessageID.IDS_FeatureSimpleLambdaParameterModifiers, diagnostics); + } } } } - - static void checkForImplicitDefault(bool hasType, ParameterSyntax param, BindingDiagnosticBag diagnostics) - { - if (!hasType && param.Default != null) - { - diagnostics.Add(ErrorCode.ERR_ImplicitlyTypedDefaultParameter, - param.Identifier.GetLocation(), param.Identifier.Text); - } - } } private UnboundLambda BindAnonymousFunction(AnonymousFunctionExpressionSyntax syntax, BindingDiagnosticBag diagnostics) @@ -350,27 +360,6 @@ private UnboundLambda BindAnonymousFunction(AnonymousFunctionExpressionSyntax sy var lambda = AnalyzeAnonymousFunction(syntax, diagnostics); var data = lambda.Data; - if (data.HasExplicitlyTypedParameterList) - { - int firstDefault = -1; - for (int i = 0; i < lambda.ParameterCount; i++) - { - // paramSyntax should not be null here; we should always be operating on an anonymous function which will have parameter information - var paramSyntax = lambda.ParameterSyntax(i); - Debug.Assert(paramSyntax is { }); - if (paramSyntax.Default != null && firstDefault == -1) - { - firstDefault = i; - } - - ParameterHelpers.GetModifiers(paramSyntax.Modifiers, refnessKeyword: out _, out var paramsKeyword, thisKeyword: out _, scope: out _); - var isParams = paramsKeyword.Kind() != SyntaxKind.None; - - // UNDONE: Where do we report improper use of pointer types? - ParameterHelpers.ReportParameterErrors(owner: null, paramSyntax, ordinal: i, lastParameterIndex: lambda.ParameterCount - 1, isParams: isParams, lambda.ParameterTypeWithAnnotations(i), - lambda.RefKind(i), containingSymbol: null, thisKeyword: default, paramsKeyword: paramsKeyword, firstDefault, diagnostics); - } - } // Parser will only have accepted static/async as allowed modifiers on this construct. // However, it may have accepted duplicates of those modifiers. Ensure that any dupes diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index e77e6b0da912a..4ead1e7410003 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -2148,18 +2148,33 @@ internal void GenerateAnonymousFunctionConversionError(BindingDiagnosticBag diag // The simplest possible case is (x, y, z)=>whatever where the target type has a ref or out parameter. var delegateParameters = delegateType.DelegateParameters(); - if (reason == LambdaConversionResult.RefInImplicitlyTypedLambda) + if (reason == LambdaConversionResult.MismatchedParameterRefKind) { for (int i = 0; i < anonymousFunction.ParameterCount; ++i) { var delegateRefKind = delegateParameters[i].RefKind; - if (delegateRefKind != RefKind.None) + var lambdaRefKind = anonymousFunction.RefKind(i); + + if (!OverloadResolution.AreRefsCompatibleForMethodConversion( + candidateMethodParameterRefKind: lambdaRefKind, + delegateParameterRefKind: delegateRefKind, + this.Compilation)) { - // Parameter {0} must be declared with the '{1}' keyword - Error(diagnostics, ErrorCode.ERR_BadParamRef, anonymousFunction.ParameterLocation(i), - i + 1, delegateRefKind.ToParameterDisplayString()); + var lambdaParameterLocation = anonymousFunction.ParameterLocation(i); + if (delegateRefKind == RefKind.None) + { + // Parameter {0} should not be declared with the '{1}' keyword + Error(diagnostics, ErrorCode.ERR_BadParamExtraRef, lambdaParameterLocation, i + 1, lambdaRefKind.ToParameterDisplayString()); + } + else + { + // Parameter {0} must be declared with the '{1}' keyword + Error(diagnostics, ErrorCode.ERR_BadParamRef, lambdaParameterLocation, i + 1, delegateRefKind.ToParameterDisplayString()); + } } } + + Debug.Assert(diagnostics.DiagnosticBag?.Count is not 0); return; } @@ -2181,16 +2196,18 @@ internal void GenerateAnonymousFunctionConversionError(BindingDiagnosticBag diag if (reason == LambdaConversionResult.MismatchedParameterType) { + // This is checked in the code that returns LambdaConversionResult.MismatchedParameterType + Debug.Assert(anonymousFunction.HasExplicitlyTypedParameterList); + // Cannot convert {0} to type '{1}' because the parameter types do not match the delegate parameter types conversionError(diagnostics, ErrorCode.ERR_CantConvAnonMethParams, id, targetType); Debug.Assert(anonymousFunction.ParameterCount == delegateParameters.Length); for (int i = 0; i < anonymousFunction.ParameterCount; ++i) { var lambdaParameterType = anonymousFunction.ParameterType(i); - if (lambdaParameterType.IsErrorType()) - { - continue; - } + + // Can't be an error type. This was already checked in a loop above this one. + Debug.Assert(!lambdaParameterType.IsErrorType()); var lambdaParameterLocation = anonymousFunction.ParameterLocation(i); var lambdaRefKind = anonymousFunction.RefKind(i); @@ -2205,19 +2222,6 @@ internal void GenerateAnonymousFunctionConversionError(BindingDiagnosticBag diag Error(diagnostics, ErrorCode.ERR_BadParamType, lambdaParameterLocation, i + 1, lambdaRefKind.ToParameterPrefix(), distinguisher.First, delegateRefKind.ToParameterPrefix(), distinguisher.Second); } - else if (lambdaRefKind != delegateRefKind) - { - if (delegateRefKind == RefKind.None) - { - // Parameter {0} should not be declared with the '{1}' keyword - Error(diagnostics, ErrorCode.ERR_BadParamExtraRef, lambdaParameterLocation, i + 1, lambdaRefKind.ToParameterDisplayString()); - } - else - { - // Parameter {0} must be declared with the '{1}' keyword - Error(diagnostics, ErrorCode.ERR_BadParamRef, lambdaParameterLocation, i + 1, delegateRefKind.ToParameterDisplayString()); - } - } } return; } diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs index 05b8dba5dc1cc..dee269ee6edde 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs @@ -1504,19 +1504,25 @@ private static LambdaConversionResult IsAnonymousFunctionCompatibleWithDelegate( return LambdaConversionResult.BadParameterCount; } - // SPEC: If F has an explicitly typed parameter list, each parameter in D has the same type - // SPEC: and modifiers as the corresponding parameter in F. - // SPEC: If F has an implicitly typed parameter list, D has no ref or out parameters. + // SPEC: If F has an implicitly or explicitly typed parameter list, each parameter in D has the same + // SPEC: type and modifiers as the corresponding parameter in F. + + for (int p = 0; p < delegateParameters.Length; ++p) + { + if (!OverloadResolution.AreRefsCompatibleForMethodConversion( + candidateMethodParameterRefKind: anonymousFunction.RefKind(p), + delegateParameterRefKind: delegateParameters[p].RefKind, + compilation)) + { + return LambdaConversionResult.MismatchedParameterRefKind; + } + } if (anonymousFunction.HasExplicitlyTypedParameterList) { for (int p = 0; p < delegateParameters.Length; ++p) { - if (!OverloadResolution.AreRefsCompatibleForMethodConversion( - candidateMethodParameterRefKind: anonymousFunction.RefKind(p), - delegateParameterRefKind: delegateParameters[p].RefKind, - compilation) || - !delegateParameters[p].Type.Equals(anonymousFunction.ParameterType(p), TypeCompareKind.AllIgnoreOptions)) + if (!delegateParameters[p].Type.Equals(anonymousFunction.ParameterType(p), TypeCompareKind.AllIgnoreOptions)) { return LambdaConversionResult.MismatchedParameterType; } @@ -1524,14 +1530,6 @@ private static LambdaConversionResult IsAnonymousFunctionCompatibleWithDelegate( } else { - for (int p = 0; p < delegateParameters.Length; ++p) - { - if (delegateParameters[p].RefKind != RefKind.None) - { - return LambdaConversionResult.RefInImplicitlyTypedLambda; - } - } - // In C# it is not possible to make a delegate type // such that one of its parameter types is a static type. But static types are // in metadata just sealed abstract types; there is nothing stopping someone in diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/LambdaConversionResult.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/LambdaConversionResult.cs index 5a10903cb935a..37597472da1e4 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/LambdaConversionResult.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/LambdaConversionResult.cs @@ -12,7 +12,7 @@ internal enum LambdaConversionResult MissingSignatureWithOutParameter, MismatchedReturnType, MismatchedParameterType, - RefInImplicitlyTypedLambda, + MismatchedParameterRefKind, StaticTypeInImplicitlyTypedLambda, ExpressionTreeMustHaveDelegateTypeArgument, ExpressionTreeFromAnonymousMethod, diff --git a/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs b/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs index f0666b06cb720..2c54c05734140 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs @@ -463,7 +463,23 @@ public bool HasExplicitReturnType(out RefKind refKind, out TypeWithAnnotations r => Data.HasExplicitReturnType(out refKind, out returnType); public Binder GetWithParametersBinder(LambdaSymbol lambdaSymbol, Binder binder) => Data.GetWithParametersBinder(lambdaSymbol, binder); - public bool HasExplicitlyTypedParameterList { get { return Data.HasExplicitlyTypedParameterList; } } + + /// + /// Whether or not the original syntax had explicit parameter list specified where all parameters had an + /// explicit type syntax included. Examples of where this is true are: `() => ...` `(int a) => ...` `(int a, + /// ref int b) => ...` and so on. + /// + /// Examples of where this is false is `a => ...` `(a) => ...` `(a, b) => ...` `(ref a) => ...` `(int a, ref b) => ...`. + /// + /// Note 1: in the case where some parameters have types and some do not, this will return false. That case is + /// an error case and an error will have already been reported to the user. In this case, we treat the + /// parameter list as if no parameter types were provided. + /// + /// Note 2: `(ref a) => ...` is legal. So this property should not be used to determine if a parameter should + /// have its ref/scoped/attributes checked. + /// + public bool HasExplicitlyTypedParameterList => Data.HasExplicitlyTypedParameterList; + public int ParameterCount { get { return Data.ParameterCount; } } public TypeWithAnnotations InferReturnType(ConversionsBase conversions, NamedTypeSymbol delegateType, ref CompoundUseSiteInfo useSiteInfo, out bool inferredFromFunctionType) => BindForReturnTypeInference(delegateType).GetInferredReturnType(conversions, _nullableState, ref useSiteInfo, out inferredFromFunctionType); @@ -477,6 +493,11 @@ public TypeWithAnnotations InferReturnType(ConversionsBase conversions, NamedTyp public SyntaxList ParameterAttributes(int index) { return Data.ParameterAttributes(index); } public TypeWithAnnotations ParameterTypeWithAnnotations(int index) { return Data.ParameterTypeWithAnnotations(index); } public TypeSymbol ParameterType(int index) { return ParameterTypeWithAnnotations(index).Type; } + + /// + /// Returns the corresponding at the given index if the lambda was declared with + /// explicit parameter syntax. + /// public ParameterSyntax? ParameterSyntax(int index) => Data.ParameterSyntax(index); public Location ParameterLocation(int index) { return Data.ParameterLocation(index); } public string ParameterName(int index) { return Data.ParameterName(index); } @@ -1527,26 +1548,26 @@ public override string ParameterName(int index) public override bool ParameterIsDiscard(int index) { + Debug.Assert(0 <= index && index < this.ParameterCount); return _parameterIsDiscardOpt.IsDefault ? false : _parameterIsDiscardOpt[index]; } public override RefKind RefKind(int index) { - Debug.Assert(0 <= index && index < _parameterTypesWithAnnotations.Length); + Debug.Assert(0 <= index && index < this.ParameterCount); return _parameterRefKinds.IsDefault ? Microsoft.CodeAnalysis.RefKind.None : _parameterRefKinds[index]; } public override ScopedKind DeclaredScope(int index) { - Debug.Assert(0 <= index && index < _parameterTypesWithAnnotations.Length); + Debug.Assert(0 <= index && index < this.ParameterCount); return _parameterDeclaredScopes.IsDefault ? ScopedKind.None : _parameterDeclaredScopes[index]; } - public override ParameterSyntax ParameterSyntax(int index) + public override ParameterSyntax? ParameterSyntax(int index) { - - Debug.Assert(_parameterSyntaxList is not null && 0 <= index && index < _parameterSyntaxList.Value.Count); - return _parameterSyntaxList.Value[index]; + Debug.Assert(0 <= index && index < this.ParameterCount); + return _parameterSyntaxList?[index]; } public override TypeWithAnnotations ParameterTypeWithAnnotations(int index) diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 0ee9bd8f3670b..fae99038193bc 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -7470,6 +7470,9 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Implicitly typed lambda parameter '{0}' cannot have a default value. + + Implicitly typed lambda parameter '{0}' cannot have the 'params' modifier. + Parameter {0} has default value '{1:10}' in lambda but '{2:10}' in the target delegate type. @@ -8029,6 +8032,9 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Element type of an iterator may not be a ref struct or a type parameter allowing ref structs + + simple lambda parameter modifiers + UnscopedRefAttribute is only valid in C# 11 or later or when targeting net7.0 or later. diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 5faced88ed48b..40ebef2a58f95 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -2357,6 +2357,7 @@ internal enum ErrorCode WRN_InterceptsLocationAttributeUnsupportedSignature = 9270, ERR_EmbeddedAttributeMustFollowPattern = 9271, + ERR_ImplicitlyTypedParamsParameter = 9272, // Note: you will need to do the following after adding errors: // 1) Update ErrorFacts.IsBuildOnlyDiagnostic (src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs) diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs index c07e760e77bbc..c3d76ed005886 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs @@ -2477,6 +2477,7 @@ or ErrorCode.WRN_AccessorDoesNotUseBackingField or ErrorCode.ERR_IteratorRefLikeElementType or ErrorCode.WRN_UnscopedRefAttributeOldRules or ErrorCode.WRN_InterceptsLocationAttributeUnsupportedSignature + or ErrorCode.ERR_ImplicitlyTypedParamsParameter => false, }; #pragma warning restore CS8524 // The switch expression does not handle some values of its input type (it is not exhaustive) involving an unnamed enum value. diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs index 2a7703792fc84..38c4399efd622 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -295,6 +295,7 @@ internal enum MessageID IDS_FeatureFirstClassSpan = MessageBase + 12849, IDS_FeatureUnboundGenericTypesInNameof = MessageBase + 12850, + IDS_FeatureSimpleLambdaParameterModifiers = MessageBase + 12851, } // Message IDs may refer to strings that need to be localized. @@ -478,6 +479,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature) case MessageID.IDS_FeatureFieldKeyword: case MessageID.IDS_FeatureFirstClassSpan: case MessageID.IDS_FeatureUnboundGenericTypesInNameof: + case MessageID.IDS_FeatureSimpleLambdaParameterModifiers: return LanguageVersion.Preview; // C# 13.0 features. diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index 02a8f83740181..12f19f4de2626 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.IO; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.PooledObjects; @@ -1363,7 +1362,7 @@ private void ParseModifiers(SyntaxListBuilder tokens, bool forAccessors, bool fo { if (!forAccessors) { - SyntaxToken scopedKeyword = ParsePossibleScopedKeyword(isFunctionPointerParameter: false); + SyntaxToken scopedKeyword = ParsePossibleScopedKeyword(isFunctionPointerParameter: false, isLambdaParameter: false); if (scopedKeyword != null) { @@ -4686,7 +4685,7 @@ private ParameterSyntax ParseParameter() var attributes = this.ParseAttributeDeclarations(inExpressionContext: false); var modifiers = _pool.Allocate(); - this.ParseParameterModifiers(modifiers, isFunctionPointerParameter: false); + this.ParseParameterModifiers(modifiers, isFunctionPointerParameter: false, isLambdaParameter: false); if (this.CurrentToken.Kind == SyntaxKind.ArgListKeyword) { @@ -4731,6 +4730,9 @@ internal static bool NoTriviaBetween(SyntaxToken token1, SyntaxToken token2) #nullable disable + private static bool IsParameterModifierIncludingScoped(SyntaxToken token) + => IsParameterModifierExcludingScoped(token) || token.ContextualKind == SyntaxKind.ScopedKeyword; + private static bool IsParameterModifierExcludingScoped(SyntaxToken token) { switch (token.Kind) @@ -4747,7 +4749,7 @@ private static bool IsParameterModifierExcludingScoped(SyntaxToken token) return false; } - private void ParseParameterModifiers(SyntaxListBuilder modifiers, bool isFunctionPointerParameter) + private void ParseParameterModifiers(SyntaxListBuilder modifiers, bool isFunctionPointerParameter, bool isLambdaParameter) { bool tryScoped = true; @@ -4763,14 +4765,14 @@ private void ParseParameterModifiers(SyntaxListBuilder modifiers, bool isFunctio if (tryScoped) { - SyntaxToken scopedKeyword = ParsePossibleScopedKeyword(isFunctionPointerParameter); + SyntaxToken scopedKeyword = ParsePossibleScopedKeyword(isFunctionPointerParameter, isLambdaParameter); if (scopedKeyword != null) { modifiers.Add(scopedKeyword); // Look if ref/out/in/readonly are next - while (this.CurrentToken.Kind is (SyntaxKind.RefKeyword or SyntaxKind.OutKeyword or SyntaxKind.InKeyword or SyntaxKind.ReadOnlyKeyword)) + while (this.CurrentToken.Kind is SyntaxKind.RefKeyword or SyntaxKind.OutKeyword or SyntaxKind.InKeyword or SyntaxKind.ReadOnlyKeyword) { modifiers.Add(this.EatToken()); } @@ -7163,7 +7165,7 @@ private ScanTypeFlags ScanFunctionPointerType(out SyntaxToken lastTokenOfType) { do { - ParseParameterModifiers(ignoredModifiers, isFunctionPointerParameter: true); + ParseParameterModifiers(ignoredModifiers, isFunctionPointerParameter: true, isLambdaParameter: false); ignoredModifiers.Clear(); _ = ScanType(out _); @@ -7722,7 +7724,7 @@ private FunctionPointerTypeSyntax ParseFunctionPointerTypeSyntax() { var modifiers = _pool.Allocate(); - ParseParameterModifiers(modifiers, isFunctionPointerParameter: true); + ParseParameterModifiers(modifiers, isFunctionPointerParameter: true, isLambdaParameter: false); types.Add(SyntaxFactory.FunctionPointerParameter( attributeLists: default, @@ -8218,7 +8220,7 @@ private bool IsPossibleLocalDeclarationStatement(bool isGlobalScriptLevel) private bool IsPossibleScopedKeyword(bool isFunctionPointerParameter) { using var _ = this.GetDisposableResetPoint(resetOnDispose: true); - return ParsePossibleScopedKeyword(isFunctionPointerParameter) != null; + return ParsePossibleScopedKeyword(isFunctionPointerParameter, isLambdaParameter: false) != null; } private bool IsPossibleFirstTypedIdentifierInLocaDeclarationStatement(bool isGlobalScriptLevel) @@ -9247,7 +9249,7 @@ private ForStatementSyntax ParseForStatement(SyntaxList att if (isDeclaration) { - return (ParseParenthesizedVariableDeclaration(VariableFlags.ForStatement, ParsePossibleScopedKeyword(isFunctionPointerParameter: false)), initializers: default); + return (ParseParenthesizedVariableDeclaration(VariableFlags.ForStatement, ParsePossibleScopedKeyword(isFunctionPointerParameter: false, isLambdaParameter: false)), initializers: default); } else if (this.CurrentToken.Kind != SyntaxKind.SemicolonToken) { @@ -9918,7 +9920,7 @@ private void ParseUsingExpression(ref VariableDeclarationSyntax declaration, ref } else { - SyntaxToken scopedKeyword = ParsePossibleScopedKeyword(isFunctionPointerParameter: false); + SyntaxToken scopedKeyword = ParsePossibleScopedKeyword(isFunctionPointerParameter: false, isLambdaParameter: false); if (scopedKeyword != null) { @@ -10059,7 +10061,7 @@ private StatementSyntax ParseLocalDeclarationStatement(SyntaxList(); try { - SyntaxToken scopedKeyword = ParsePossibleScopedKeyword(isFunctionPointerParameter: false); + SyntaxToken scopedKeyword = ParsePossibleScopedKeyword(isFunctionPointerParameter: false, isLambdaParameter: false); // For local functions, 'scoped' is a modifier in LocalFunctionStatementSyntax if (scopedKeyword != null) @@ -10133,34 +10135,60 @@ private StatementSyntax ParseLocalDeclarationStatement(SyntaxList` + // 2. this is a parameter `scoped T x`. + + if (isFunctionPointerParameter) + { + return this.CurrentToken.Kind is SyntaxKind.CommaToken or SyntaxKind.GreaterThanToken; + } + else if (this.CurrentToken.Kind == SyntaxKind.IdentifierToken) + { + return true; + } + + return false; + } } private VariableDesignationSyntax ParseDesignation(bool forPattern) @@ -12147,91 +12175,42 @@ private RefValueExpressionSyntax ParseRefValueExpression() private bool ScanParenthesizedLambda(Precedence precedence) { - return ScanParenthesizedImplicitlyTypedLambda(precedence) || ScanExplicitlyTypedLambda(precedence); + return ScanImplicitlyTypedLambdaOrSimpleExplicitlyTypedParenthesizedLambda(precedence) || ScanExplicitlyTypedLambda(precedence); } - private bool ScanParenthesizedImplicitlyTypedLambda(Precedence precedence) + /// + /// Scans implicitly typed lambdas (like (a, b) =>) as well as basic explicitly typed lambdas (like + /// (A a, B b) =>). More complex scanning of parenthesized lambdas happens in . + /// + private bool ScanImplicitlyTypedLambdaOrSimpleExplicitlyTypedParenthesizedLambda(Precedence precedence) { Debug.Assert(CurrentToken.Kind == SyntaxKind.OpenParenToken); - if (!(precedence <= Precedence.Lambda)) - { + if (precedence > Precedence.Lambda) return false; - } - // case 1: ( x , - if (isParenVarCommaSyntax()) - { - // Make sure it really looks like a lambda, not just a tuple - int curTk = 3; - while (true) - { - var tk = this.PeekToken(curTk++); - // skip identifiers commas and predefined types in any combination for error recovery - if (tk.Kind is not SyntaxKind.IdentifierToken and not SyntaxKind.CommaToken - && !SyntaxFacts.IsPredefinedType(tk.Kind) - && (this.IsInQuery || !IsTokenQueryContextualKeyword(tk))) - { - break; - }; - } - - // ) => - return this.PeekToken(curTk - 1).Kind == SyntaxKind.CloseParenToken && - this.PeekToken(curTk).Kind == SyntaxKind.EqualsGreaterThanToken; - } - - // case 2: ( x ) => - if (IsTrueIdentifier(this.PeekToken(1))) - { - // allow for a) => - var skipIndex = 2; - - // Must have: ) => - if (this.PeekToken(skipIndex).Kind == SyntaxKind.CloseParenToken - && this.PeekToken(skipIndex + 1).Kind == SyntaxKind.EqualsGreaterThanToken) - { - return true; - } - } + // ( ) => + // ( x ) => or ( ref x ) => + // ( x , ... ) => or ( ref x , ...) => + var index = 1; - // case 3: ( ) => - if (this.PeekToken(1).Kind == SyntaxKind.CloseParenToken - && this.PeekToken(2).Kind == SyntaxKind.EqualsGreaterThanToken) - { - return true; - } - - // case 4: ( params - // This case is interesting in that it is not legal; this error could be caught at parse time but we would rather - // recover from the error and let the semantic analyzer deal with it. - if (this.PeekToken(1).Kind == SyntaxKind.ParamsKeyword) - { - return true; - } - - return false; - - bool isParenVarCommaSyntax() + while (true) { - var token1 = this.PeekToken(1); + var token = this.PeekToken(index++); - // Ensure next token is a variable - if (token1.Kind == SyntaxKind.IdentifierToken) + // Keep skipping modifiers, commas, and identifiers to consume the rest of the lambda arguments. Note: + // this *will* grab explicitly typed lambdas like `(A b) =>`. However, that's ok. The only caller of + // this is ScanParenthesizedLambda, which just wants to know if it's on some form of lambda. + if (this.IsTrueIdentifier(token) || + token.Kind is SyntaxKind.CommaToken || + IsParameterModifierIncludingScoped(token)) { - if (!this.IsInQuery || !IsTokenQueryContextualKeyword(token1)) - { - // Variable must be directly followed by a comma if not followed by exclamation - var token2 = this.PeekToken(2); - // ( x , [...] - if (token2.Kind == SyntaxKind.CommaToken) - { - return true; - } - } + continue; } - return false; + return token.Kind == SyntaxKind.CloseParenToken && + this.PeekToken(index).Kind == SyntaxKind.EqualsGreaterThanToken; } } @@ -12239,7 +12218,7 @@ private bool ScanExplicitlyTypedLambda(Precedence precedence) { Debug.Assert(CurrentToken.Kind == SyntaxKind.OpenParenToken); - if (!(precedence <= Precedence.Lambda)) + if (precedence > Precedence.Lambda) { return false; } @@ -12263,21 +12242,17 @@ private bool ScanExplicitlyTypedLambda(Precedence precedence) ParseAttributeDeclarations(inExpressionContext: true); - bool hasModifier = false; - if (IsParameterModifierExcludingScoped(this.CurrentToken) || this.CurrentToken.ContextualKind == SyntaxKind.ScopedKeyword) + if (IsParameterModifierIncludingScoped(this.CurrentToken)) { SyntaxListBuilder modifiers = _pool.Allocate(); - ParseParameterModifiers(modifiers, isFunctionPointerParameter: false); - hasModifier = modifiers.Count != 0; + ParseParameterModifiers(modifiers, isFunctionPointerParameter: false, isLambdaParameter: true); _pool.Free(modifiers); } - if (hasModifier || ShouldParseLambdaParameterType()) + if (ShouldParseLambdaParameterType() && + this.ScanType() == ScanTypeFlags.NotType) { - if (this.ScanType() == ScanTypeFlags.NotType) - { - return false; - } + return false; } // eat the parameter name. @@ -13441,13 +13416,12 @@ private ParameterSyntax ParseLambdaParameter() // Params are actually illegal in a lambda, but we'll allow it for error recovery purposes and // give the "params unexpected" error at semantic analysis time. SyntaxListBuilder modifiers = _pool.Allocate(); - if (IsParameterModifierExcludingScoped(this.CurrentToken) || this.CurrentToken.ContextualKind == SyntaxKind.ScopedKeyword) + if (IsParameterModifierIncludingScoped(this.CurrentToken)) { - ParseParameterModifiers(modifiers, isFunctionPointerParameter: false); + ParseParameterModifiers(modifiers, isFunctionPointerParameter: false, isLambdaParameter: true); } - // If we have "scoped/ref/out/in/params" always try to parse out a type. - var paramType = modifiers.Count != 0 || ShouldParseLambdaParameterType() + var paramType = ShouldParseLambdaParameterType() ? ParseType(ParseTypeMode.Parameter) : null; @@ -13497,11 +13471,12 @@ private bool ShouldParseLambdaParameterType() // // In all other cases, parse out a type. var peek1 = this.PeekToken(1); - if (peek1.Kind != SyntaxKind.CommaToken && - peek1.Kind != SyntaxKind.CloseParenToken && - peek1.Kind != SyntaxKind.EqualsGreaterThanToken && - peek1.Kind != SyntaxKind.OpenBraceToken && - peek1.Kind != SyntaxKind.EqualsToken) + if (peek1.Kind + is not SyntaxKind.CommaToken + and not SyntaxKind.CloseParenToken + and not SyntaxKind.EqualsGreaterThanToken + and not SyntaxKind.OpenBraceToken + and not SyntaxKind.EqualsToken) { return true; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs index c35f8ca4b7677..7e3f09b39ca49 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs @@ -345,35 +345,20 @@ private ImmutableArray MakeParameters( for (int p = 0; p < unboundLambda.ParameterCount; ++p) { + var refKind = unboundLambda.RefKind(p); + var scope = unboundLambda.DeclaredScope(p); + var paramSyntax = unboundLambda.ParameterSyntax(p); + // If there are no types given in the lambda then use the delegate type. // If the lambda is typed then the types probably match the delegate types; // if they do not, use the lambda types for binding. Either way, if we // can, then we use the lambda types. (Whatever you do, do not use the names // in the delegate parameters; they are not in scope!) - - TypeWithAnnotations type; - RefKind refKind; - ScopedKind scope; - ParameterSyntax? paramSyntax = null; - if (hasExplicitlyTypedParameterList) - { - type = unboundLambda.ParameterTypeWithAnnotations(p); - refKind = unboundLambda.RefKind(p); - scope = unboundLambda.DeclaredScope(p); - paramSyntax = unboundLambda.ParameterSyntax(p); - } - else if (p < numDelegateParameters) - { - type = parameterTypes[p]; - refKind = RefKind.None; - scope = ScopedKind.None; - } - else - { - type = TypeWithAnnotations.Create(new ExtendedErrorTypeSymbol(compilation, name: string.Empty, arity: 0, errorInfo: null)); - refKind = RefKind.None; - scope = ScopedKind.None; - } + var type = hasExplicitlyTypedParameterList + ? unboundLambda.ParameterTypeWithAnnotations(p) + : p < numDelegateParameters + ? parameterTypes[p] + : TypeWithAnnotations.Create(new ExtendedErrorTypeSymbol(compilation, name: string.Empty, arity: 0, errorInfo: null)); var attributeLists = unboundLambda.ParameterAttributes(p); var name = unboundLambda.ParameterName(p); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs b/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs index f09261eaf06ab..06f45033425c7 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs @@ -556,6 +556,11 @@ internal static void CheckParameterModifiers( if (parsingLambdaParams) { MessageID.IDS_FeatureLambdaParamsArray.CheckFeatureAvailability(diagnostics, modifier); + + if (parameter is ParameterSyntax { Type: null, Identifier.Text: var parameterIdentifier }) + { + diagnostics.Add(ErrorCode.ERR_ImplicitlyTypedParamsParameter, modifier, parameterIdentifier); + } } break; @@ -643,6 +648,8 @@ static void addERR_ParamsCantBeWithModifier(BindingDiagnosticBag diagnostics, Sy } } + /// Will be if this is called for a lambda parameter + /// with an explicit type provided. public static void ReportParameterErrors( Symbol? owner, BaseParameterSyntax syntax, @@ -673,7 +680,7 @@ public static void ReportParameterErrors( // error CS1670: params is not valid in this context diagnostics.Add(ErrorCode.ERR_IllegalParams, paramsKeyword.GetLocation()); } - else if (typeWithAnnotations.IsStatic) + else if (!typeWithAnnotations.IsDefault && typeWithAnnotations.IsStatic) { Debug.Assert(containingSymbol is null || (containingSymbol is FunctionPointerMethodSymbol or { ContainingType: not null })); // error CS0721: '{0}': static types cannot be used as parameters @@ -689,6 +696,7 @@ public static void ReportParameterErrors( diagnostics.Add(ErrorCode.ERR_DefaultValueBeforeRequiredValue, loc); } else if (refKind != RefKind.None && + !typeWithAnnotations.IsDefault && typeWithAnnotations.IsRestrictedType(ignoreSpanLikeTypes: true)) { // CS1601: Cannot make reference to variable of type 'System.TypedReference' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index cbe7e5519a53a..07288d72a065d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -977,6 +977,11 @@ Implicitně zadaný parametr lambda {0} nemůže mít výchozí hodnotu. + + Implicitly typed lambda parameter '{0}' cannot have the 'params' modifier. + Implicitly typed lambda parameter '{0}' cannot have the 'params' modifier. + + Arguments with 'in' modifier cannot be used in dynamically dispatched expressions. Argumenty s modifikátorem in se nedají použít v dynamicky volaných výrazech. @@ -2577,6 +2582,11 @@ zapečetěný ToString v záznamu + + simple lambda parameter modifiers + simple lambda parameter modifiers + + string escape character řídicí znak řetězce @@ -13231,4 +13241,4 @@ Pokud chcete odstranit toto varování, můžete místo toho použít /reference - + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index ffad83e6f3ec7..62b899d5cfe08 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -977,6 +977,11 @@ Der implizit eingegebene Parameter „{0}“ der Lambdafunktion darf keinen Standardwert aufweisen. + + Implicitly typed lambda parameter '{0}' cannot have the 'params' modifier. + Implicitly typed lambda parameter '{0}' cannot have the 'params' modifier. + + Arguments with 'in' modifier cannot be used in dynamically dispatched expressions. Argumente mit dem Modifizierer "in" können nicht in dynamisch gebundenen Ausdrücken verwendet werden. @@ -2577,6 +2582,11 @@ versiegelte "ToString" im Datensatz + + simple lambda parameter modifiers + simple lambda parameter modifiers + + string escape character Zeichenfolge – Escapezeichen @@ -13231,4 +13241,4 @@ Um die Warnung zu beheben, können Sie stattdessen /reference verwenden (Einbett - + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index a8174eb9e0764..530edb9e6b677 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -977,6 +977,11 @@ El parámetro lambda con tipo implícito “{0}” no puede tener un valor predeterminado. + + Implicitly typed lambda parameter '{0}' cannot have the 'params' modifier. + Implicitly typed lambda parameter '{0}' cannot have the 'params' modifier. + + Arguments with 'in' modifier cannot be used in dynamically dispatched expressions. No se pueden usar argumentos con el modificador "in" en expresiones distribuidas dinámicamente. @@ -2577,6 +2582,11 @@ ToString sellado en el registro + + simple lambda parameter modifiers + simple lambda parameter modifiers + + string escape character carácter de escape de cadena @@ -13231,4 +13241,4 @@ Para eliminar la advertencia puede usar /reference (establezca la propiedad Embe - + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index beabb32c64bea..9a6cb087b309f 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -977,6 +977,11 @@ Le paramètre d’expression lambda implicitement typé '{0}' ne peut pas avoir de valeur par défaut. + + Implicitly typed lambda parameter '{0}' cannot have the 'params' modifier. + Implicitly typed lambda parameter '{0}' cannot have the 'params' modifier. + + Arguments with 'in' modifier cannot be used in dynamically dispatched expressions. Impossible d'utiliser les arguments avec le modificateur 'in' dans les expressions dispatchées dynamiquement. @@ -2577,6 +2582,11 @@ ToString scellé dans l’enregistrement + + simple lambda parameter modifiers + simple lambda parameter modifiers + + string escape character caractère d’échappement de chaîne @@ -13231,4 +13241,4 @@ Pour supprimer l'avertissement, vous pouvez utiliser la commande /reference (dé - + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index dbf05f0098b60..2a712a023a6a5 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -977,6 +977,11 @@ Il parametro lambda tipizzato in modo implicito '{0}' non può avere un valore predefinito. + + Implicitly typed lambda parameter '{0}' cannot have the 'params' modifier. + Implicitly typed lambda parameter '{0}' cannot have the 'params' modifier. + + Arguments with 'in' modifier cannot be used in dynamically dispatched expressions. Non è possibile usare argomenti con il modificatore 'in' nelle espressioni inviate in modo dinamico. @@ -2577,6 +2582,11 @@ ToString sealed nel record + + simple lambda parameter modifiers + simple lambda parameter modifiers + + string escape character carattere di escape stringa @@ -13231,4 +13241,4 @@ Per rimuovere l'avviso, è invece possibile usare /reference (impostare la propr - + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 91967e679e57e..e3e411d0ec55f 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -977,6 +977,11 @@ 暗黙的に型指定されたラムダパラメーター '{0}' に既定値を指定することはできません。 + + Implicitly typed lambda parameter '{0}' cannot have the 'params' modifier. + Implicitly typed lambda parameter '{0}' cannot have the 'params' modifier. + + Arguments with 'in' modifier cannot be used in dynamically dispatched expressions. 'in' 修飾子を持つ引数を、動的ディスパッチされる式で使用することはできません。 @@ -2577,6 +2582,11 @@ レコードでシールされた ToString + + simple lambda parameter modifiers + simple lambda parameter modifiers + + string escape character 文字列エスケープ文字 @@ -13231,4 +13241,4 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ - + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 96067188dd6e7..68efe3dbfcf1c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -977,6 +977,11 @@ 암시적 형식 람다 매개 변수 '{0}'은(는) 기본값을 가질 수 없습니다. + + Implicitly typed lambda parameter '{0}' cannot have the 'params' modifier. + Implicitly typed lambda parameter '{0}' cannot have the 'params' modifier. + + Arguments with 'in' modifier cannot be used in dynamically dispatched expressions. 동적으로 디스패치된 식에서 'in' 한정자가 있는 인수를 사용할 수 없습니다. @@ -2577,6 +2582,11 @@ 레코드의 봉인된 ToString + + simple lambda parameter modifiers + simple lambda parameter modifiers + + string escape character 문자열 이스케이프 문자 @@ -13231,4 +13241,4 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ - + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 355af1a80b33c..306b023e608bc 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -977,6 +977,11 @@ Niejawnie typizowany parametr wyrażenia lambda „{0}” nie może mieć wartości domyślnej. + + Implicitly typed lambda parameter '{0}' cannot have the 'params' modifier. + Implicitly typed lambda parameter '{0}' cannot have the 'params' modifier. + + Arguments with 'in' modifier cannot be used in dynamically dispatched expressions. Nie można używać argumentów z modyfikatorem „in” w wyrażeniach przydzielanych dynamicznie. @@ -2577,6 +2582,11 @@ zapieczętowany obiekt ToString w rekordzie + + simple lambda parameter modifiers + simple lambda parameter modifiers + + string escape character ciąg — znak ucieczki @@ -13231,4 +13241,4 @@ Aby usunąć ostrzeżenie, możesz zamiast tego użyć opcji /reference (ustaw w - + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 2be5bdbea8bc3..6ecb18f565952 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -977,6 +977,11 @@ O parâmetro lambda '{0}' digitado implicitamente não pode ter um valor padrão. + + Implicitly typed lambda parameter '{0}' cannot have the 'params' modifier. + Implicitly typed lambda parameter '{0}' cannot have the 'params' modifier. + + Arguments with 'in' modifier cannot be used in dynamically dispatched expressions. Os argumentos com o modificador 'in' não podem ser usados em expressões vinculadas dinamicamente. @@ -2577,6 +2582,11 @@ ToString selado no registro + + simple lambda parameter modifiers + simple lambda parameter modifiers + + string escape character cadeia de caracteres de escape @@ -13231,4 +13241,4 @@ Para incorporar informações de tipo de interoperabilidade para os dois assembl - + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 1225df19a66b1..c22cc8562dd5c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -977,6 +977,11 @@ Неявно типизованный параметр "{0}" не может иметь значение по умолчанию. + + Implicitly typed lambda parameter '{0}' cannot have the 'params' modifier. + Implicitly typed lambda parameter '{0}' cannot have the 'params' modifier. + + Arguments with 'in' modifier cannot be used in dynamically dispatched expressions. Аргументы с модификатором "in" невозможно использовать в динамически диспетчеризируемых выражениях. @@ -2577,6 +2582,11 @@ запечатанный ToString в записи + + simple lambda parameter modifiers + simple lambda parameter modifiers + + string escape character escape-символ строки @@ -13232,4 +13242,4 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ - + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 473a1e172bf44..e3a6e50d861a6 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -977,6 +977,11 @@ Türü örtük olarak belirlenmiş lambda '{0}' parametresi varsayılan bir değere sahip olamaz. + + Implicitly typed lambda parameter '{0}' cannot have the 'params' modifier. + Implicitly typed lambda parameter '{0}' cannot have the 'params' modifier. + + Arguments with 'in' modifier cannot be used in dynamically dispatched expressions. 'in' değiştiricisine sahip bağımsız değişkenler dinamik olarak dağıtılan ifadelerde kullanılamaz. @@ -2577,6 +2582,11 @@ kayıtta mühürlü ToString + + simple lambda parameter modifiers + simple lambda parameter modifiers + + string escape character dize kaçış karakteri @@ -13231,4 +13241,4 @@ Uyarıyı kaldırmak için, /reference kullanabilirsiniz (Birlikte Çalışma T - + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 7fe80cfa424fb..3d04b9aa2634f 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -977,6 +977,11 @@ 隐式键入的 lambda 参数 "{0}" 不能具有默认值。 + + Implicitly typed lambda parameter '{0}' cannot have the 'params' modifier. + Implicitly typed lambda parameter '{0}' cannot have the 'params' modifier. + + Arguments with 'in' modifier cannot be used in dynamically dispatched expressions. 带有 "in" 修饰符的参数不能用于动态调度的表达式。 @@ -2577,6 +2582,11 @@ 记录的密封 ToString + + simple lambda parameter modifiers + simple lambda parameter modifiers + + string escape character 字符串转义字符 @@ -13231,4 +13241,4 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ - + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 5e9289fe0d0b7..622f4ee2405d4 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -977,6 +977,11 @@ 隱含輸入的 Lambda 參數 '{0}' 不能有預設值。 + + Implicitly typed lambda parameter '{0}' cannot have the 'params' modifier. + Implicitly typed lambda parameter '{0}' cannot have the 'params' modifier. + + Arguments with 'in' modifier cannot be used in dynamically dispatched expressions. 具有 'in' 修飾元的引數不可用於動態分派的運算式。 @@ -2577,6 +2582,11 @@ 記錄中有密封的 ToString + + simple lambda parameter modifiers + simple lambda parameter modifiers + + string escape character 字串逸出字元 @@ -13231,4 +13241,4 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ - + \ No newline at end of file diff --git a/src/Compilers/CSharp/Test/Emit3/RefReadonlyParameterTests.cs b/src/Compilers/CSharp/Test/Emit3/RefReadonlyParameterTests.cs index da033366bf9cc..55808b02151ef 100644 --- a/src/Compilers/CSharp/Test/Emit3/RefReadonlyParameterTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/RefReadonlyParameterTests.cs @@ -774,9 +774,6 @@ public static void M(ref readonly int x) { } // (7,25): error CS1676: Parameter 1 must be declared with the 'ref readonly' keyword // Expression e4 = (int p) => C.M(in p); Diagnostic(ErrorCode.ERR_BadParamRef, "p").WithArguments("1", "ref readonly").WithLocation(7, 25), - // (7,28): error CS1661: Cannot convert lambda expression to type 'Expression' because the parameter types do not match the delegate parameter types - // Expression e4 = (int p) => C.M(in p); - Diagnostic(ErrorCode.ERR_CantConvAnonMethParams, "=>").WithArguments("lambda expression", "System.Linq.Expressions.Expression").WithLocation(7, 28), // (8,49): error CS1615: Argument 1 may not be passed with the 'out' keyword // Expression> e5 = (int p) => C.M(out p); Diagnostic(ErrorCode.ERR_BadArgExtraRef, "p").WithArguments("1", "out").WithLocation(8, 49)); @@ -8419,30 +8416,18 @@ public void Conversion_Lambda() // (7,11): error CS1676: Parameter 1 must be declared with the 'ref readonly' keyword // rr = (int x) => throw null; Diagnostic(ErrorCode.ERR_BadParamRef, "x").WithArguments("1", "ref readonly").WithLocation(7, 11), - // (7,14): error CS1661: Cannot convert lambda expression to type 'RR' because the parameter types do not match the delegate parameter types - // rr = (int x) => throw null; - Diagnostic(ErrorCode.ERR_CantConvAnonMethParams, "=>").WithArguments("lambda expression", "RR").WithLocation(7, 14), // (8,15): error CS1676: Parameter 1 must be declared with the 'ref readonly' keyword // rr = (ref int x) => throw null; Diagnostic(ErrorCode.ERR_BadParamRef, "x").WithArguments("1", "ref readonly").WithLocation(8, 15), - // (8,18): error CS1661: Cannot convert lambda expression to type 'RR' because the parameter types do not match the delegate parameter types - // rr = (ref int x) => throw null; - Diagnostic(ErrorCode.ERR_CantConvAnonMethParams, "=>").WithArguments("lambda expression", "RR").WithLocation(8, 18), // (9,6): warning CS9198: Reference kind modifier of parameter 'in int x' doesn't match the corresponding parameter 'ref readonly int p' in target. // rr = (in int x) => throw null; Diagnostic(ErrorCode.WRN_TargetDifferentRefness, "(in int x) => throw null").WithArguments("in int x", "ref readonly int p").WithLocation(9, 6), // (10,15): error CS1676: Parameter 1 must be declared with the 'ref readonly' keyword // rr = (out int x) => throw null; Diagnostic(ErrorCode.ERR_BadParamRef, "x").WithArguments("1", "ref readonly").WithLocation(10, 15), - // (10,18): error CS1661: Cannot convert lambda expression to type 'RR' because the parameter types do not match the delegate parameter types - // rr = (out int x) => throw null; - Diagnostic(ErrorCode.ERR_CantConvAnonMethParams, "=>").WithArguments("lambda expression", "RR").WithLocation(10, 18), // (11,23): error CS1677: Parameter 1 should not be declared with the 'ref readonly' keyword // v = (ref readonly int x) => throw null; Diagnostic(ErrorCode.ERR_BadParamExtraRef, "x").WithArguments("1", "ref readonly").WithLocation(11, 23), - // (11,26): error CS1661: Cannot convert lambda expression to type 'V' because the parameter types do not match the delegate parameter types - // v = (ref readonly int x) => throw null; - Diagnostic(ErrorCode.ERR_CantConvAnonMethParams, "=>").WithArguments("lambda expression", "V").WithLocation(11, 26), // (12,5): warning CS9198: Reference kind modifier of parameter 'ref readonly int x' doesn't match the corresponding parameter 'in int p' in target. // i = (ref readonly int x) => throw null; Diagnostic(ErrorCode.WRN_TargetDifferentRefness, "(ref readonly int x) => throw null").WithArguments("ref readonly int x", "in int p").WithLocation(12, 5), @@ -8452,24 +8437,15 @@ public void Conversion_Lambda() // (14,23): error CS1676: Parameter 1 must be declared with the 'out' keyword // o = (ref readonly int x) => throw null; Diagnostic(ErrorCode.ERR_BadParamRef, "x").WithArguments("1", "out").WithLocation(14, 23), - // (14,26): error CS1661: Cannot convert lambda expression to type 'O' because the parameter types do not match the delegate parameter types - // o = (ref readonly int x) => throw null; - Diagnostic(ErrorCode.ERR_CantConvAnonMethParams, "=>").WithArguments("lambda expression", "O").WithLocation(14, 26), // (15,5): warning CS9198: Reference kind modifier of parameter 'in int x' doesn't match the corresponding parameter 'ref int p' in target. // r = (in int x) => throw null; Diagnostic(ErrorCode.WRN_TargetDifferentRefness, "(in int x) => throw null").WithArguments("in int x", "ref int p").WithLocation(15, 5), // (16,14): error CS1676: Parameter 1 must be declared with the 'in' keyword // i = (ref int x) => throw null; Diagnostic(ErrorCode.ERR_BadParamRef, "x").WithArguments("1", "in").WithLocation(16, 14), - // (16,17): error CS1661: Cannot convert lambda expression to type 'I' because the parameter types do not match the delegate parameter types - // i = (ref int x) => throw null; - Diagnostic(ErrorCode.ERR_CantConvAnonMethParams, "=>").WithArguments("lambda expression", "I").WithLocation(16, 17), // (18,11): error CS1676: Parameter 1 must be declared with the 'ref readonly' keyword // rr = (int x) => throw null; Diagnostic(ErrorCode.ERR_BadParamRef, "x").WithArguments("1", "ref readonly").WithLocation(18, 11), - // (18,14): error CS1661: Cannot convert lambda expression to type 'RR' because the parameter types do not match the delegate parameter types - // rr = (int x) => throw null; - Diagnostic(ErrorCode.ERR_CantConvAnonMethParams, "=>").WithArguments("lambda expression", "RR").WithLocation(18, 14), // (19,6): error CS1676: Parameter 1 must be declared with the 'ref readonly' keyword // rr = x => throw null; Diagnostic(ErrorCode.ERR_BadParamRef, "x").WithArguments("1", "ref readonly").WithLocation(19, 6)); @@ -8502,10 +8478,7 @@ string getSource(string body) => $$""" CreateCompilation(source1).VerifyDiagnostics( // (8,18): error CS1676: Parameter 1 must be declared with the 'ref readonly' keyword // x = (X)((ref int p) => System.Console.Write("3")); - Diagnostic(ErrorCode.ERR_BadParamRef, "p").WithArguments("1", "ref readonly").WithLocation(8, 18), - // (8,21): error CS1661: Cannot convert lambda expression to type 'X' because the parameter types do not match the delegate parameter types - // x = (X)((ref int p) => System.Console.Write("3")); - Diagnostic(ErrorCode.ERR_CantConvAnonMethParams, "=>").WithArguments("lambda expression", "X").WithLocation(8, 21)); + Diagnostic(ErrorCode.ERR_BadParamRef, "p").WithArguments("1", "ref readonly").WithLocation(8, 18)); } else { @@ -8578,19 +8551,13 @@ public class C CreateCompilation(source3, new[] { comp1Ref }, parseOptions: TestOptions.Regular11).VerifyDiagnostics( // (1,14): error CS1676: Parameter 1 must be declared with the 'ref readonly' keyword // C.X((ref int p) => System.Console.Write("4")); - Diagnostic(ErrorCode.ERR_BadParamRef, "p").WithArguments("1", "ref readonly").WithLocation(1, 14), - // (1,17): error CS1661: Cannot convert lambda expression to type 'X' because the parameter types do not match the delegate parameter types - // C.X((ref int p) => System.Console.Write("4")); - Diagnostic(ErrorCode.ERR_CantConvAnonMethParams, "=>").WithArguments("lambda expression", "X").WithLocation(1, 17)); + Diagnostic(ErrorCode.ERR_BadParamRef, "p").WithArguments("1", "ref readonly").WithLocation(1, 14)); expectedDiagnostics = new[] { // (1,14): error CS1676: Parameter 1 must be declared with the 'ref readonly' keyword // C.X((ref int p) => System.Console.Write("4")); - Diagnostic(ErrorCode.ERR_BadParamRef, "p").WithArguments("1", "ref readonly").WithLocation(1, 14), - // (1,17): error CS1661: Cannot convert lambda expression to type 'X' because the parameter types do not match the delegate parameter types - // C.X((ref int p) => System.Console.Write("4")); - Diagnostic(ErrorCode.ERR_CantConvAnonMethParams, "=>").WithArguments("lambda expression", "X").WithLocation(1, 17) + Diagnostic(ErrorCode.ERR_BadParamRef, "p").WithArguments("1", "ref readonly").WithLocation(1, 14) }; CreateCompilation(source3, new[] { comp1Ref }, parseOptions: TestOptions.Regular12).VerifyDiagnostics(expectedDiagnostics); @@ -8649,15 +8616,9 @@ public class C // (4,13): error CS1676: Parameter 1 must be declared with the 'ref' keyword // C.X((in int p) => System.Console.Write("3")); Diagnostic(ErrorCode.ERR_BadParamRef, "p").WithArguments("1", "ref").WithLocation(4, 13), - // (4,16): error CS1661: Cannot convert lambda expression to type 'X' because the parameter types do not match the delegate parameter types - // C.X((in int p) => System.Console.Write("3")); - Diagnostic(ErrorCode.ERR_CantConvAnonMethParams, "=>").WithArguments("lambda expression", "X").WithLocation(4, 16), // (5,14): error CS1676: Parameter 1 must be declared with the 'in' keyword // C.Y((ref int p) => System.Console.Write("4")); - Diagnostic(ErrorCode.ERR_BadParamRef, "p").WithArguments("1", "in").WithLocation(5, 14), - // (5,17): error CS1661: Cannot convert lambda expression to type 'Y' because the parameter types do not match the delegate parameter types - // C.Y((ref int p) => System.Console.Write("4")); - Diagnostic(ErrorCode.ERR_CantConvAnonMethParams, "=>").WithArguments("lambda expression", "Y").WithLocation(5, 17)); + Diagnostic(ErrorCode.ERR_BadParamRef, "p").WithArguments("1", "in").WithLocation(5, 14)); var expectedDiagnostics = new[] { @@ -8675,10 +8636,7 @@ public class C { // (1,14): error CS1676: Parameter 1 must be declared with the 'in' keyword // C.Y((ref int p) => System.Console.Write("4")); - Diagnostic(ErrorCode.ERR_BadParamRef, "p").WithArguments("1", "in").WithLocation(1, 14), - // (1,17): error CS1661: Cannot convert lambda expression to type 'Y' because the parameter types do not match the delegate parameter types - // C.Y((ref int p) => System.Console.Write("4")); - Diagnostic(ErrorCode.ERR_CantConvAnonMethParams, "=>").WithArguments("lambda expression", "Y").WithLocation(1, 17) + Diagnostic(ErrorCode.ERR_BadParamRef, "p").WithArguments("1", "in").WithLocation(1, 14) }; CreateCompilation(source3, new[] { comp1Ref }, parseOptions: TestOptions.Regular12).VerifyDiagnostics(expectedDiagnostics); @@ -8704,9 +8662,6 @@ static void Main() CreateCompilation(source).VerifyDiagnostics( // (7,22): error CS1676: Parameter 1 must be declared with the 'in' keyword // d = (ref int x) => { x = 42; }; // should be an error - Diagnostic(ErrorCode.ERR_BadParamRef, "x").WithArguments("1", "in").WithLocation(7, 22), - // (7,25): error CS1661: Cannot convert lambda expression to type '' because the parameter types do not match the delegate parameter types - // d = (ref int x) => { x = 42; }; // should be an error - Diagnostic(ErrorCode.ERR_CantConvAnonMethParams, "=>").WithArguments("lambda expression", "").WithLocation(7, 25)); + Diagnostic(ErrorCode.ERR_BadParamRef, "x").WithArguments("1", "in").WithLocation(7, 22)); } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs index eb2b674266f78..9226f1e71e1e3 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs @@ -21,7 +21,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests { - public class LambdaTests : SemanticModelTestBase + public sealed class LambdaTests : SemanticModelTestBase { [Fact, WorkItem(37456, "https://github.com/dotnet/roslyn/issues/37456")] public void Verify37456() @@ -173,12 +173,9 @@ void M() // (20,45): error CS1593: Delegate 'Func' does not take 1 arguments // Func q5 = (System.Duobel x5)=>1; // but arity error should not be suppressed on error type Diagnostic(ErrorCode.ERR_BadDelArgCount, "=>").WithArguments("System.Func", "1").WithLocation(20, 45), - // (21,52): error CS1661: Cannot convert lambda expression to type 'C.D1' because the parameter types do not match the delegate parameter types + // (21,25): error CS1676: Parameter 1 must be declared with the 'ref' keyword // D1 q6 = (double x6, ref int y6, ref int z6)=>1; - Diagnostic(ErrorCode.ERR_CantConvAnonMethParams, "=>").WithArguments("lambda expression", "C.D1").WithLocation(21, 52), - // (21,25): error CS1678: Parameter 1 is declared as type 'double' but should be 'ref int' - // D1 q6 = (double x6, ref int y6, ref int z6)=>1; - Diagnostic(ErrorCode.ERR_BadParamType, "x6").WithArguments("1", "", "double", "ref ", "int").WithLocation(21, 25), + Diagnostic(ErrorCode.ERR_BadParamRef, "x6").WithArguments("1", "ref").WithLocation(21, 25), // (21,37): error CS1676: Parameter 2 must be declared with the 'out' keyword // D1 q6 = (double x6, ref int y6, ref int z6)=>1; Diagnostic(ErrorCode.ERR_BadParamRef, "y6").WithArguments("2", "out").WithLocation(21, 37), @@ -6887,10 +6884,7 @@ static void Main() Diagnostic(ErrorCode.ERR_BadParameterModifiers, "ref").WithArguments("ref", "out").WithLocation(8, 21), // (8,29): error CS1676: Parameter 1 must be declared with the 'ref' keyword // D d3 = (out ref int i) => { }; - Diagnostic(ErrorCode.ERR_BadParamRef, "i").WithArguments("1", "ref").WithLocation(8, 29), - // (8,32): error CS1661: Cannot convert lambda expression to type 'D' because the parameter types do not match the delegate parameter types - // D d3 = (out ref int i) => { }; - Diagnostic(ErrorCode.ERR_CantConvAnonMethParams, "=>").WithArguments("lambda expression", "D").WithLocation(8, 32)); + Diagnostic(ErrorCode.ERR_BadParamRef, "i").WithArguments("1", "ref").WithLocation(8, 29)); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); @@ -7098,8 +7092,7 @@ static void M(D1 d1) { } Diagnostic(ErrorCode.ERR_BadParamRef, "r1").WithArguments("1", "ref").WithLocation(9, 17), // (10,11): error CS1676: Parameter 1 must be declared with the 'ref' keyword // M(r2 => r2); // 2 - Diagnostic(ErrorCode.ERR_BadParamRef, "r2").WithArguments("1", "ref").WithLocation(10, 11) - ); + Diagnostic(ErrorCode.ERR_BadParamRef, "r2").WithArguments("1", "ref").WithLocation(10, 11)); var syntaxTree = comp.SyntaxTrees[0]; var root = syntaxTree.GetRoot(); @@ -7203,10 +7196,7 @@ public static void Main(string[] args) Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "(string s = null, x = 7, double d = 3.14) => { }").WithLocation(5, 19), // (5,37): error CS0748: Inconsistent lambda parameter usage; parameter types must be all explicit or all implicit // var lam = (string s = null, x = 7, double d = 3.14) => { }; - Diagnostic(ErrorCode.ERR_InconsistentLambdaParameterUsage, "x").WithLocation(5, 37), - // (5,37): error CS9098: Implicitly typed lambda parameter 'x' cannot have a default value. - // var lam = (string s = null, x = 7, double d = 3.14) => { }; - Diagnostic(ErrorCode.ERR_ImplicitlyTypedDefaultParameter, "x").WithArguments("x").WithLocation(5, 37)); + Diagnostic(ErrorCode.ERR_InconsistentLambdaParameterUsage, "x").WithLocation(5, 37)); } [Fact] @@ -8554,6 +8544,255 @@ public void ParamsArray_ThisModifier_02() Diagnostic(ErrorCode.ERR_ThisInBadContext, "this").WithLocation(1, 19)); } + [Fact] + public void ImplicitlyTypedLambdaWithModifier_CSharp13() + { + var source = """ + delegate void D(ref int i); + + class C + { + void M() + { + D d = (ref a) => { }; + } + } + """; + var compilation = CreateCompilation(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (7,16): error CS8652: The feature 'simple lambda parameter modifiers' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // D d = (ref a) => { }; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "ref a").WithArguments("simple lambda parameter modifiers").WithLocation(7, 16)); + + var tree = compilation.SyntaxTrees[0]; + var semanticModel = compilation.GetSemanticModel(tree); + + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol; + + Assert.Equal(RefKind.Ref, symbol.Parameters[0].RefKind); + Assert.Equal(SpecialType.System_Int32, symbol.Parameters[0].Type.SpecialType); + } + + [Fact] + public void IncorrectlyTypedLambdaWithCorrectRefNess() + { + var source = """ + delegate void D(ref int i); + + class C + { + void M() + { + D d = (ref byte a) => { }; + } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (7,25): error CS1678: Parameter 1 is declared as type 'ref byte' but should be 'ref int' + // D d = (ref byte a) => { }; + Diagnostic(ErrorCode.ERR_BadParamType, "a").WithArguments("1", "ref ", "byte", "ref ", "int").WithLocation(7, 25), + // (7,28): error CS1661: Cannot convert lambda expression to type 'D' because the parameter types do not match the delegate parameter types + // D d = (ref byte a) => { }; + Diagnostic(ErrorCode.ERR_CantConvAnonMethParams, "=>").WithArguments("lambda expression", "D").WithLocation(7, 28)); + } + + [Theory, CombinatorialData] + public void TestParamsWithImplicitExplicitLambdas( + [CombinatorialValues("params", "")] string delegateModifier, + [CombinatorialValues("params", "")] string lambdaModifier, + [CombinatorialValues("int[]", "")] string lambdaType, + bool isCSharp13) + { + var options = isCSharp13 ? TestOptions.Regular13 : TestOptions.RegularNext; + var source = $$""" + delegate void D({{delegateModifier}} int[] i); + + class C + { + void M() + { + D d = ({{lambdaModifier}} {{lambdaType}} a) => { }; + } + } + """; + var compilation = CreateCompilation(source, parseOptions: options); + + var diagnostics = new List(); + + if (lambdaModifier == "params") + { + if (lambdaType is "") + { + diagnostics.Add( + // (7,16): error CS9272: Implicitly typed lambda parameter 'a' cannot have the 'params' modifier. + // D d = (params a) => { }; + Diagnostic(ErrorCode.ERR_ImplicitlyTypedParamsParameter, "params").WithArguments("a").WithLocation(7, 16)); + + if (isCSharp13) + { + diagnostics.Add( + // (7,16): error CS8652: The feature 'simple lambda parameter modifiers' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // D d = (params a) => { }; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "params a").WithArguments("simple lambda parameter modifiers")); + } + } + + if (delegateModifier == "") + { + diagnostics.Add( + // (7,24): warning CS9100: Parameter 1 has params modifier in lambda but not in target delegate type. + // D d = (params a) => { }; + Diagnostic(ErrorCode.WRN_ParamsArrayInLambdaOnly, "a").WithArguments("1")); + } + } + + compilation.VerifyDiagnostics([.. diagnostics]); + } + + [Fact] + public void TestScopedImplicitParameter_CSharp10() + { + var source = $$""" + using System; + + class scoped { } + + class C + { + void M() + { + Action f = (scoped a) => { }; + } + } + """; + var compilation = CreateCompilation(source, parseOptions: TestOptions.Regular10).VerifyDiagnostics( + // (3,7): warning CS8981: The type name 'scoped' only contains lower-cased ascii characters. Such names may become reserved for the language. + // class scoped { } + Diagnostic(ErrorCode.WRN_LowerCaseTypeName, "scoped").WithArguments("scoped").WithLocation(3, 7)); + + var tree = compilation.SyntaxTrees[0]; + var semanticModel = compilation.GetSemanticModel(tree); + var lambda = tree.GetRoot().DescendantNodes().OfType().Single(); + + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol; + Assert.Equal(ScopedKind.None, symbol.Parameters[0].ScopedKind); + } + + [Fact] + public void TestScopedImplicitParameter_CSharp13_1() + { + var source = $$""" + using System; + + class scoped { } + + class C + { + void M() + { + Action f = (scoped a) => { }; + } + } + """; + var compilation = CreateCompilation(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (3,7): error CS9062: Types and aliases cannot be named 'scoped'. + // class scoped { } + Diagnostic(ErrorCode.ERR_ScopedTypeNameDisallowed, "scoped").WithLocation(3, 7)); + + var tree = compilation.SyntaxTrees[0]; + var semanticModel = compilation.GetSemanticModel(tree); + var lambda = tree.GetRoot().DescendantNodes().OfType().Single(); + + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol; + Assert.Equal(ScopedKind.None, symbol.Parameters[0].ScopedKind); + Assert.Equal("scoped", symbol.Parameters[0].Type.Name); + } + + [Fact] + public void TestScopedImplicitParameter_CSharp13_2() + { + var source = $$""" + using System; + + class @scoped { } + + class C + { + void M() + { + Action f = (scoped a) => { }; + } + } + """; + var compilation = CreateCompilation(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics(); + + var tree = compilation.SyntaxTrees[0]; + var semanticModel = compilation.GetSemanticModel(tree); + var lambda = tree.GetRoot().DescendantNodes().OfType().Single(); + + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol; + Assert.Equal(ScopedKind.None, symbol.Parameters[0].ScopedKind); + Assert.Equal("scoped", symbol.Parameters[0].Type.Name); + } + + [Fact] + public void TestScopedImplicitParameter1() + { + var source = $$""" + class scoped { } + ref struct S { } + delegate void D(S s); + + class C + { + void M() + { + D f = (scoped a) => { }; + } + } + """; + var compilation = CreateCompilation(source).VerifyDiagnostics( + // (1,7): error CS9062: Types and aliases cannot be named 'scoped'. + // class scoped { } + Diagnostic(ErrorCode.ERR_ScopedTypeNameDisallowed, "scoped").WithLocation(1, 7)); + + var tree = compilation.SyntaxTrees[0]; + var semanticModel = compilation.GetSemanticModel(tree); + var lambda = tree.GetRoot().DescendantNodes().OfType().Single(); + + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol; + Assert.Equal(ScopedKind.ScopedValue, symbol.Parameters[0].ScopedKind); + Assert.Equal("S", symbol.Parameters[0].Type.Name); + } + + [Fact] + public void TestScopedImplicitParameter2() + { + var source = $$""" + class @scoped { } + ref struct S { } + delegate void D(S s); + + class C + { + void M() + { + D f = (scoped a) => { }; + } + } + """; + var compilation = CreateCompilation(source).VerifyDiagnostics(); + + var tree = compilation.SyntaxTrees[0]; + var semanticModel = compilation.GetSemanticModel(tree); + var lambda = tree.GetRoot().DescendantNodes().OfType().Single(); + + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol; + Assert.Equal(ScopedKind.ScopedValue, symbol.Parameters[0].ScopedKind); + Assert.Equal("S", symbol.Parameters[0].Type.Name); + } + [Fact] public void CompilerLoweringPreserveAttribute_01() { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs index 5b9edc3c4fc13..62502d7002009 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -11250,7 +11250,7 @@ static void F6(scoped in R r) { } } [Fact] - public void ParameterScope_08() + public void ParameterScope_08_CSharp13() { var source = @"ref struct R { } @@ -11263,74 +11263,79 @@ static void Main() var f3 = (scoped scoped ref R r) => { }; } }"; - var comp = CreateCompilation(source); + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular13); comp.VerifyEmitDiagnostics( - // (6,19): error CS0103: The name 'scoped' does not exist in the current context - // var f1 = (scoped scoped R r) => { }; - Diagnostic(ErrorCode.ERR_NameNotInContext, "scoped").WithArguments("scoped").WithLocation(6, 19), - // (6,26): error CS1026: ) expected - // var f1 = (scoped scoped R r) => { }; - Diagnostic(ErrorCode.ERR_CloseParenExpected, "scoped").WithLocation(6, 26), - // (6,26): error CS1002: ; expected + // (6,18): error CS8917: The delegate type could not be inferred. // var f1 = (scoped scoped R r) => { }; - Diagnostic(ErrorCode.ERR_SemicolonExpected, "scoped").WithLocation(6, 26), - // (6,35): warning CS0168: The variable 'r' is declared but never used + Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "(scoped scoped R r) => { }").WithLocation(6, 18), + // (6,26): error CS0246: The type or namespace name 'scoped' could not be found (are you missing a using directive or an assembly reference?) // var f1 = (scoped scoped R r) => { }; - Diagnostic(ErrorCode.WRN_UnreferencedVar, "r").WithArguments("r").WithLocation(6, 35), - // (6,36): error CS1003: Syntax error, ',' expected + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "scoped").WithArguments("scoped").WithLocation(6, 26), + // (6,35): error CS1003: Syntax error, ',' expected // var f1 = (scoped scoped R r) => { }; - Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments(",").WithLocation(6, 36), - // (6,41): error CS1002: ; expected + Diagnostic(ErrorCode.ERR_SyntaxError, "r").WithArguments(",").WithLocation(6, 35), + // (6,35): error CS0748: Inconsistent lambda parameter usage; parameter types must be all explicit or all implicit // var f1 = (scoped scoped R r) => { }; - Diagnostic(ErrorCode.ERR_SemicolonExpected, "{").WithLocation(6, 41), - // (7,19): error CS1525: Invalid expression term 'ref' - // var f2 = (ref scoped scoped R r) => { }; - Diagnostic(ErrorCode.ERR_InvalidExprTerm, "ref scoped").WithArguments("ref").WithLocation(7, 19), - // (7,19): error CS1073: Unexpected token 'ref' - // var f2 = (ref scoped scoped R r) => { }; - Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(7, 19), - // (7,30): error CS1026: ) expected + Diagnostic(ErrorCode.ERR_InconsistentLambdaParameterUsage, "r").WithLocation(6, 35), + // (7,23): error CS0246: The type or namespace name 'scoped' could not be found (are you missing a using directive or an assembly reference?) // var f2 = (ref scoped scoped R r) => { }; - Diagnostic(ErrorCode.ERR_CloseParenExpected, "scoped").WithLocation(7, 30), - // (7,30): error CS1002: ; expected + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "scoped").WithArguments("scoped").WithLocation(7, 23), + // (7,37): error CS1003: Syntax error, ',' expected // var f2 = (ref scoped scoped R r) => { }; - Diagnostic(ErrorCode.ERR_SemicolonExpected, "scoped").WithLocation(7, 30), - // (7,39): error CS0128: A local variable or function named 'r' is already defined in this scope - // var f2 = (ref scoped scoped R r) => { }; - Diagnostic(ErrorCode.ERR_LocalDuplicate, "r").WithArguments("r").WithLocation(7, 39), - // (7,39): warning CS0168: The variable 'r' is declared but never used - // var f2 = (ref scoped scoped R r) => { }; - Diagnostic(ErrorCode.WRN_UnreferencedVar, "r").WithArguments("r").WithLocation(7, 39), - // (7,40): error CS1003: Syntax error, ',' expected - // var f2 = (ref scoped scoped R r) => { }; - Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments(",").WithLocation(7, 40), - // (7,45): error CS1002: ; expected - // var f2 = (ref scoped scoped R r) => { }; - Diagnostic(ErrorCode.ERR_SemicolonExpected, "{").WithLocation(7, 45), - // (8,19): error CS0103: The name 'scoped' does not exist in the current context - // var f3 = (scoped scoped ref R r) => { }; - Diagnostic(ErrorCode.ERR_NameNotInContext, "scoped").WithArguments("scoped").WithLocation(8, 19), - // (8,26): error CS1026: ) expected + Diagnostic(ErrorCode.ERR_SyntaxError, "R").WithArguments(",").WithLocation(7, 37), + // (8,19): error CS0246: The type or namespace name 'scoped' could not be found (are you missing a using directive or an assembly reference?) // var f3 = (scoped scoped ref R r) => { }; - Diagnostic(ErrorCode.ERR_CloseParenExpected, "scoped").WithLocation(8, 26), - // (8,26): error CS1002: ; expected + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "scoped").WithArguments("scoped").WithLocation(8, 19), + // (8,33): error CS1003: Syntax error, ',' expected // var f3 = (scoped scoped ref R r) => { }; - Diagnostic(ErrorCode.ERR_SemicolonExpected, "scoped").WithLocation(8, 26), - // (8,39): error CS0128: A local variable or function named 'r' is already defined in this scope - // var f3 = (scoped scoped ref R r) => { }; - Diagnostic(ErrorCode.ERR_LocalDuplicate, "r").WithArguments("r").WithLocation(8, 39), - // (8,39): error CS8174: A declaration of a by-reference variable must have an initializer - // var f3 = (scoped scoped ref R r) => { }; - Diagnostic(ErrorCode.ERR_ByReferenceVariableMustBeInitialized, "r").WithLocation(8, 39), - // (8,39): warning CS0168: The variable 'r' is declared but never used + Diagnostic(ErrorCode.ERR_SyntaxError, "ref").WithArguments(",").WithLocation(8, 33)); + } + + [Fact] + public void ParameterScope_08_CSharp14() + { + var source = + """ + ref struct R { } + class Program + { + static void Main() + { + var f1 = (scoped scoped R r) => { }; + var f2 = (ref scoped scoped R r) => { }; + var f3 = (scoped scoped ref R r) => { }; + } + } + """; + var comp = CreateCompilation(source, parseOptions: TestOptions.RegularNext); + comp.VerifyEmitDiagnostics( + // (6,18): error CS8917: The delegate type could not be inferred. + // var f1 = (scoped scoped R r) => { }; + Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "(scoped scoped R r) => { }").WithLocation(6, 18), + // (6,26): error CS0246: The type or namespace name 'scoped' could not be found (are you missing a using directive or an assembly reference?) + // var f1 = (scoped scoped R r) => { }; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "scoped").WithArguments("scoped").WithLocation(6, 26), + // (6,35): error CS1003: Syntax error, ',' expected + // var f1 = (scoped scoped R r) => { }; + Diagnostic(ErrorCode.ERR_SyntaxError, "r").WithArguments(",").WithLocation(6, 35), + // (6,35): error CS0748: Inconsistent lambda parameter usage; parameter types must be all explicit or all implicit + // var f1 = (scoped scoped R r) => { }; + Diagnostic(ErrorCode.ERR_InconsistentLambdaParameterUsage, "r").WithLocation(6, 35), + // (7,23): error CS0246: The type or namespace name 'scoped' could not be found (are you missing a using directive or an assembly reference?) + // var f2 = (ref scoped scoped R r) => { }; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "scoped").WithArguments("scoped").WithLocation(7, 23), + // (7,37): error CS1003: Syntax error, ',' expected + // var f2 = (ref scoped scoped R r) => { }; + Diagnostic(ErrorCode.ERR_SyntaxError, "R").WithArguments(",").WithLocation(7, 37), + // (8,26): error CS0246: The type or namespace name 'scoped' could not be found (are you missing a using directive or an assembly reference?) // var f3 = (scoped scoped ref R r) => { }; - Diagnostic(ErrorCode.WRN_UnreferencedVar, "r").WithArguments("r").WithLocation(8, 39), - // (8,40): error CS1003: Syntax error, ',' expected + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "scoped").WithArguments("scoped").WithLocation(8, 26), + // (8,33): error CS1001: Identifier expected // var f3 = (scoped scoped ref R r) => { }; - Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments(",").WithLocation(8, 40), - // (8,45): error CS1002: ; expected + Diagnostic(ErrorCode.ERR_IdentifierExpected, "ref").WithLocation(8, 33), + // (8,33): error CS1003: Syntax error, ',' expected // var f3 = (scoped scoped ref R r) => { }; - Diagnostic(ErrorCode.ERR_SemicolonExpected, "{").WithLocation(8, 45)); + Diagnostic(ErrorCode.ERR_SyntaxError, "ref").WithArguments(",").WithLocation(8, 33)); } [Fact] @@ -22154,31 +22159,18 @@ void M() "; comp = CreateCompilation(source); comp.VerifyDiagnostics( - // (6,13): error CS1525: Invalid expression term 'void' - // _ = void (ref scoped R parameter) => throw null; - Diagnostic(ErrorCode.ERR_InvalidExprTerm, "void").WithArguments("void").WithLocation(6, 13), - // (6,23): error CS0103: The name 'scoped' does not exist in the current context - // _ = void (ref scoped R parameter) => throw null; - Diagnostic(ErrorCode.ERR_NameNotInContext, "scoped").WithArguments("scoped").WithLocation(6, 23), - // (6,30): error CS1003: Syntax error, ',' expected + // (6,9): error CS8183: Cannot infer the type of implicitly-typed discard. // _ = void (ref scoped R parameter) => throw null; - Diagnostic(ErrorCode.ERR_SyntaxError, "R").WithArguments(",").WithLocation(6, 30), - // (6,30): error CS0119: 'R' is a type, which is not valid in the given context + Diagnostic(ErrorCode.ERR_DiscardTypeInferenceFailed, "_").WithLocation(6, 9), + // (6,23): error CS0246: The type or namespace name 'scoped' could not be found (are you missing a using directive or an assembly reference?) // _ = void (ref scoped R parameter) => throw null; - Diagnostic(ErrorCode.ERR_BadSKunknown, "R").WithArguments("R", "type").WithLocation(6, 30), + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "scoped").WithArguments("scoped").WithLocation(6, 23), // (6,32): error CS1003: Syntax error, ',' expected // _ = void (ref scoped R parameter) => throw null; Diagnostic(ErrorCode.ERR_SyntaxError, "parameter").WithArguments(",").WithLocation(6, 32), - // (6,32): error CS0103: The name 'parameter' does not exist in the current context - // _ = void (ref scoped R parameter) => throw null; - Diagnostic(ErrorCode.ERR_NameNotInContext, "parameter").WithArguments("parameter").WithLocation(6, 32), - // (6,43): error CS1002: ; expected + // (6,32): error CS0748: Inconsistent lambda parameter usage; parameter types must be all explicit or all implicit // _ = void (ref scoped R parameter) => throw null; - Diagnostic(ErrorCode.ERR_SemicolonExpected, "=>").WithLocation(6, 43), - // (6,43): error CS1513: } expected - // _ = void (ref scoped R parameter) => throw null; - Diagnostic(ErrorCode.ERR_RbraceExpected, "=>").WithLocation(6, 43) - ); + Diagnostic(ErrorCode.ERR_InconsistentLambdaParameterUsage, "parameter").WithLocation(6, 32)); } [Theory, WorkItem(62931, "https://github.com/dotnet/roslyn/issues/62931")] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs index bffc78b186f0e..c3a4b47e22d63 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs @@ -16311,9 +16311,6 @@ static void Main() "; var compilation = CreateCompilation(text); compilation.VerifyDiagnostics( - // (7,13): error CS1661: Cannot convert anonymous method to type 'E' because the parameter types do not match the delegate parameter types - // E e = delegate(out int i) { }; // CS1676 - Diagnostic(ErrorCode.ERR_CantConvAnonMethParams, "delegate").WithArguments("anonymous method", "E").WithLocation(7, 13), // (7,30): error CS1676: Parameter 1 must be declared with the 'ref' keyword // E e = delegate(out int i) { }; // CS1676 Diagnostic(ErrorCode.ERR_BadParamRef, "i").WithArguments("1", "ref").WithLocation(7, 30), @@ -16338,18 +16335,12 @@ static void Main() "; var compilation = CreateCompilation(text); compilation.VerifyDiagnostics( - // (7,15): error CS1661: Cannot convert anonymous method to type 'D' because the parameter types do not match the delegate parameter types - // D d = delegate(out int i) { }; // CS1677 - Diagnostic(ErrorCode.ERR_CantConvAnonMethParams, "delegate").WithArguments("anonymous method", "D").WithLocation(7, 15), // (7,32): error CS1677: Parameter 1 should not be declared with the 'out' keyword // D d = delegate(out int i) { }; // CS1677 Diagnostic(ErrorCode.ERR_BadParamExtraRef, "i").WithArguments("1", "out").WithLocation(7, 32), // (8,11): error CS0128: A local variable or function named 'd' is already defined in this scope // D d = delegate(ref int j) { }; // CS1677 Diagnostic(ErrorCode.ERR_LocalDuplicate, "d").WithArguments("d").WithLocation(8, 11), - // (8,15): error CS1661: Cannot convert anonymous method to type 'D' because the parameter types do not match the delegate parameter types - // D d = delegate(ref int j) { }; // CS1677 - Diagnostic(ErrorCode.ERR_CantConvAnonMethParams, "delegate").WithArguments("anonymous method", "D").WithLocation(8, 15), // (8,32): error CS1677: Parameter 1 should not be declared with the 'ref' keyword // D d = delegate(ref int j) { }; // CS1677 Diagnostic(ErrorCode.ERR_BadParamExtraRef, "j").WithArguments("1", "ref").WithLocation(8, 32), diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/SimpleLambdaParametersWithModifiersTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/SimpleLambdaParametersWithModifiersTests.cs new file mode 100644 index 0000000000000..79356cf1ccfda --- /dev/null +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/SimpleLambdaParametersWithModifiersTests.cs @@ -0,0 +1,1458 @@ +// 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. + +using System; +using System.Linq; +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.CSharp.UnitTests; +using Microsoft.CodeAnalysis.Operations; +using Microsoft.CodeAnalysis.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.Semantic.UnitTests.Semantics; + +public sealed class SimpleLambdaParametersWithModifiersTests : SemanticModelTestBase +{ + [Fact] + public void TestOneParameterWithRef() + { + var compilation = CreateCompilation(""" + delegate void D(ref int x); + + class C + { + void M() + { + D d = (ref x) => { }; + } + } + """).VerifyDiagnostics(); + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol!; + + Assert.Equal(MethodKind.LambdaMethod, symbol.MethodKind); + Assert.Equal(RefKind.Ref, symbol.Parameters.Single().RefKind); + Assert.Equal(SpecialType.System_Int32, symbol.Parameters.Single().Type.SpecialType); + } + + [Fact] + public void TestTwoParametersWithRef() + { + var compilation = CreateCompilation(""" + delegate void D(string s, ref int x); + + class C + { + void M() + { + D d = (s, ref x) => { }; + } + } + """).VerifyDiagnostics(); + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol!; + + Assert.Equal(MethodKind.LambdaMethod, symbol.MethodKind); + Assert.True(symbol.Parameters is [{ Type.SpecialType: SpecialType.System_String, RefKind: RefKind.None }, { Type.SpecialType: SpecialType.System_Int32, RefKind: RefKind.Ref }]); + } + + [Fact] + public void TestTwoParametersWithRefAndOptionalValue() + { + var compilation = CreateCompilation(""" + delegate void D(string s, ref int x); + + class C + { + void M() + { + D d = (s, ref x = 1) => { }; + } + } + """).VerifyDiagnostics( + // (7,19): error CS1741: A ref or out parameter cannot have a default value + // D d = (s, ref x = 1) => { }; + Diagnostic(ErrorCode.ERR_RefOutDefaultValue, "ref").WithLocation(7, 19), + // (7,23): error CS9098: Implicitly typed lambda parameter 'x' cannot have a default value. + // D d = (s, ref x = 1) => { }; + Diagnostic(ErrorCode.ERR_ImplicitlyTypedDefaultParameter, "x").WithArguments("x").WithLocation(7, 23)); + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol!; + + Assert.Equal(MethodKind.LambdaMethod, symbol.MethodKind); + Assert.True(symbol.Parameters is [ + { Type.SpecialType: SpecialType.System_String, RefKind: RefKind.None }, + { Type.SpecialType: SpecialType.System_Int32, RefKind: RefKind.Ref, IsOptional: false }]); + } + + [Fact] + public void TestOneParameterWithAnAttribute() + { + var compilation = CreateCompilation(""" + using System; + + delegate void D(ref int x); + + class C + { + void M() + { + D d = ([CLSCompliant(false)] ref x) => { }; + } + } + """).VerifyDiagnostics(); + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol!; + + Assert.Equal(MethodKind.LambdaMethod, symbol.MethodKind); + Assert.Equal( + symbol.Parameters.Single().GetAttributes().Single().AttributeClass, + compilation.GetTypeByMetadataName(typeof(CLSCompliantAttribute).FullName).GetPublicSymbol()); + } + + [Fact] + public void TestOneParameterWithAnAttributeAndDefaultValue() + { + var compilation = CreateCompilation(""" + using System; + + delegate void D(ref int x); + + class C + { + void M() + { + D d = ([CLSCompliant(false)] ref x = 0) => { }; + } + } + """).VerifyDiagnostics( + // (9,38): error CS1741: A ref or out parameter cannot have a default value + // D d = ([CLSCompliant(false)] ref x = 0) => { }; + Diagnostic(ErrorCode.ERR_RefOutDefaultValue, "ref").WithLocation(9, 38), + // (9,42): error CS9098: Implicitly typed lambda parameter 'x' cannot have a default value. + // D d = ([CLSCompliant(false)] ref x = 0) => { }; + Diagnostic(ErrorCode.ERR_ImplicitlyTypedDefaultParameter, "x").WithArguments("x").WithLocation(9, 42)); + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol!; + + Assert.Equal(MethodKind.LambdaMethod, symbol.MethodKind); + Assert.Equal( + symbol.Parameters.Single().GetAttributes().Single().AttributeClass, + compilation.GetTypeByMetadataName(typeof(CLSCompliantAttribute).FullName).GetPublicSymbol()); + Assert.True(symbol.Parameters is [{ Type.SpecialType: SpecialType.System_Int32, RefKind: RefKind.Ref, IsOptional: false }]); + } + + [Theory] + [InlineData("[CLSCompliant(false), My]")] + [InlineData("[CLSCompliant(false)][My]")] + public void TestOneParameterWithMultipleAttribute(string attributeForm) + { + var compilation = CreateCompilation($$""" + using System; + + [AttributeUsage(AttributeTargets.Parameter)] + class MyAttribute : Attribute; + delegate void D(ref int x); + + class C + { + void M() + { + D d = ({{attributeForm}} ref x) => { }; + } + } + """).VerifyDiagnostics(); + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol!; + + Assert.Equal(MethodKind.LambdaMethod, symbol.MethodKind); + Assert.True( + symbol.Parameters.Single().GetAttributes().Any(a => a.AttributeClass!.Equals( + compilation.GetTypeByMetadataName(typeof(CLSCompliantAttribute).FullName).GetPublicSymbol()))); + Assert.True( + symbol.Parameters.Single().GetAttributes().Any(a => a.AttributeClass!.Equals( + compilation.GetTypeByMetadataName("MyAttribute").GetPublicSymbol()))); + } + + [Fact] + public void TestOneParameterWithScoped() + { + var compilation = CreateCompilationWithSpan(""" + using System; + delegate void D(scoped ReadOnlySpan x); + + class C + { + void M() + { + D d = (scoped x) => { }; + } + } + """).VerifyDiagnostics(); + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol!; + + Assert.Equal(MethodKind.LambdaMethod, symbol.MethodKind); + Assert.Equal(ScopedKind.ScopedValue, symbol.Parameters.Single().ScopedKind); + Assert.Equal(compilation.GetTypeByMetadataName(typeof(ReadOnlySpan<>).FullName).GetPublicSymbol(), symbol.Parameters.Single().Type.OriginalDefinition); + } + + [Fact] + public void TestTwoParametersWithScopedAndRef1() + { + var compilation = CreateCompilationWithSpan(""" + using System; + delegate void D(scoped ReadOnlySpan a, ref ReadOnlySpan b); + + class C + { + void M() + { + D d = (scoped a, ref b) => { }; + } + } + """).VerifyDiagnostics(); + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol!; + + Assert.Equal(MethodKind.LambdaMethod, symbol.MethodKind); + Assert.Equal(ScopedKind.ScopedValue, symbol.Parameters.First().ScopedKind); + Assert.Equal(compilation.GetTypeByMetadataName(typeof(ReadOnlySpan<>).FullName).GetPublicSymbol(), symbol.Parameters.First().Type.OriginalDefinition); + } + + [Fact] + public void TestTwoParametersWithScopedAndRef2() + { + var compilation = CreateCompilationWithSpan(""" + using System; + delegate void D(scoped ReadOnlySpan a, ref ReadOnlySpan b); + + class C + { + void M() + { + D d = (a, ref b) => { }; + } + } + """).VerifyDiagnostics( + // (8,15): error CS8986: The 'scoped' modifier of parameter 'a' doesn't match target 'D'. + // D d = (a, ref b) => { }; + Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfTarget, "(a, ref b) => { }").WithArguments("a", "D").WithLocation(8, 15)); + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol!; + + Assert.Equal(MethodKind.LambdaMethod, symbol.MethodKind); + Assert.Equal(ScopedKind.None, symbol.Parameters.First().ScopedKind); + Assert.Equal(compilation.GetTypeByMetadataName(typeof(ReadOnlySpan<>).FullName).GetPublicSymbol(), symbol.Parameters.First().Type.OriginalDefinition); + } + + [Fact] + public void TestOneParameterWithScopedAndOptionalValue() + { + var compilation = CreateCompilationWithSpan(""" + using System; + delegate void D(scoped ReadOnlySpan x); + + class C + { + void M() + { + D d = (scoped x = default) => { }; + } + } + """).VerifyDiagnostics( + // (8,23): error CS9098: Implicitly typed lambda parameter 'x' cannot have a default value. + // D d = (scoped x = default) => { }; + Diagnostic(ErrorCode.ERR_ImplicitlyTypedDefaultParameter, "x").WithArguments("x").WithLocation(8, 23), + // (8,23): warning CS9099: Parameter 1 has default value 'null' in lambda but '' in the target delegate type. + // D d = (scoped x = default) => { }; + Diagnostic(ErrorCode.WRN_OptionalParamValueMismatch, "x").WithArguments("1", "null", "").WithLocation(8, 23)); + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol!; + + Assert.Equal(MethodKind.LambdaMethod, symbol.MethodKind); + Assert.Equal(ScopedKind.ScopedValue, symbol.Parameters.Single().ScopedKind); + Assert.True(symbol.Parameters.Single().IsOptional); + Assert.Equal(compilation.GetTypeByMetadataName(typeof(ReadOnlySpan<>).FullName).GetPublicSymbol(), symbol.Parameters.Single().Type.OriginalDefinition); + } + + [Theory, CombinatorialData] + public void TestOneParameterWithScopedAsParameterName(bool escaped) + { + var compilation = CreateCompilationWithSpan($$""" + using System; + delegate void D(scoped ReadOnlySpan x); + + class C + { + void M() + { + D d = (scoped {{(escaped ? "@" : "")}}scoped) => { }; + } + } + """).VerifyDiagnostics(); + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol!; + + Assert.Equal(MethodKind.LambdaMethod, symbol.MethodKind); + Assert.Equal(ScopedKind.ScopedValue, symbol.Parameters.Single().ScopedKind); + Assert.Equal("scoped", symbol.Parameters.Single().Name); + Assert.Equal(compilation.GetTypeByMetadataName(typeof(ReadOnlySpan<>).FullName).GetPublicSymbol(), symbol.Parameters.Single().Type.OriginalDefinition); + } + + [Fact] + public void TestInconsistentUseOfTypes() + { + var compilation = CreateCompilation(""" + delegate void D(string s, ref int x); + + class C + { + void M() + { + D d = (string s, ref x) => { }; + } + } + """).VerifyDiagnostics( + // (7,30): error CS0748: Inconsistent lambda parameter usage; parameter types must be all explicit or all implicit + // D d = (string s, ref x) => { }; + Diagnostic(ErrorCode.ERR_InconsistentLambdaParameterUsage, "x").WithLocation(7, 30)); + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol!; + + Assert.Equal(MethodKind.LambdaMethod, symbol.MethodKind); + Assert.True(symbol.Parameters is [ + { Type.SpecialType: SpecialType.System_String, RefKind: RefKind.None }, + { Type.SpecialType: SpecialType.System_Int32, RefKind: RefKind.Ref }]); + } + + [Fact] + public void TestOneParameterWithNoModifiersAndOptionalValue() + { + var compilation = CreateCompilation(""" + delegate void D(int x); + + class C + { + void M() + { + D d = (x = 1) => { }; + } + } + """).VerifyDiagnostics( + // (7,16): error CS9098: Implicitly typed lambda parameter 'x' cannot have a default value. + // D d = (x = 1) => { }; + Diagnostic(ErrorCode.ERR_ImplicitlyTypedDefaultParameter, "x").WithArguments("x").WithLocation(7, 16), + // (7,16): warning CS9099: Parameter 1 has default value '1' in lambda but '' in the target delegate type. + // D d = (x = 1) => { }; + Diagnostic(ErrorCode.WRN_OptionalParamValueMismatch, "x").WithArguments("1", "1", "").WithLocation(7, 16)); + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol!; + + Assert.Equal(MethodKind.LambdaMethod, symbol.MethodKind); + Assert.True(symbol.Parameters is [{ Type.SpecialType: SpecialType.System_Int32, RefKind: RefKind.None, IsOptional: true }]); + } + + [Fact] + public void TestNonParenthesizedLambdaPrecededByRef() + { + var compilation = CreateCompilation(""" + delegate void D(ref int x); + + class C + { + void M() + { + D d = ref x => { }; + } + } + """).VerifyDiagnostics( + // (7,11): error CS8171: Cannot initialize a by-value variable with a reference + // D d = ref x => { }; + Diagnostic(ErrorCode.ERR_InitializeByValueVariableWithReference, "d = ref x => { }").WithLocation(7, 11), + // (7,19): error CS1676: Parameter 1 must be declared with the 'ref' keyword + // D d = ref x => { }; + Diagnostic(ErrorCode.ERR_BadParamRef, "x").WithArguments("1", "ref").WithLocation(7, 19)); + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol!; + + Assert.Equal(MethodKind.LambdaMethod, symbol.MethodKind); + Assert.True(symbol.Parameters is [{ Type: IErrorTypeSymbol, RefKind: RefKind.None, IsOptional: false }]); + } + + [Fact] + public void TestRefParameterMissingName() + { + var compilation = CreateCompilation(""" + delegate void D(ref int x); + + class C + { + void M() + { + D d = (ref) => { }; + } + } + """).VerifyDiagnostics( + // (7,19): error CS1001: Identifier expected + // D d = (ref) => { }; + Diagnostic(ErrorCode.ERR_IdentifierExpected, ")").WithLocation(7, 19)); + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol!; + + Assert.Equal(MethodKind.LambdaMethod, symbol.MethodKind); + Assert.True(symbol.Parameters is [{ Name: "", Type.SpecialType: SpecialType.System_Int32, RefKind: RefKind.Ref, IsOptional: false }]); + } + + [Fact] + public void TestAnonymousMethodWithRefParameter() + { + var compilation = CreateCompilation(""" + delegate void D(ref int x); + + class C + { + void M() + { + D d = delegate (ref x) { }; + } + } + """).VerifyDiagnostics( + // (7,29): error CS0246: The type or namespace name 'x' could not be found (are you missing a using directive or an assembly reference?) + // D d = delegate (ref x) { }; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "x").WithArguments("x").WithLocation(7, 29), + // (7,30): error CS1001: Identifier expected + // D d = delegate (ref x) { }; + Diagnostic(ErrorCode.ERR_IdentifierExpected, ")").WithLocation(7, 30)); + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol!; + + Assert.Equal(MethodKind.LambdaMethod, symbol.MethodKind); + Assert.True(symbol.Parameters is [{ Name: "", Type: IErrorTypeSymbol { Name: "x" }, RefKind: RefKind.Ref, IsOptional: false }]); + } + + [Fact] + public void TestLocalFunctionWithRefParameter() + { + var compilation = CreateCompilation(""" + class C + { + void M() + { + void LocalFunc(ref x) { }; + } + } + """).VerifyDiagnostics( + // (5,14): warning CS8321: The local function 'LocalFunc' is declared but never used + // void LocalFunc(ref x) { }; + Diagnostic(ErrorCode.WRN_UnreferencedLocalFunction, "LocalFunc").WithArguments("LocalFunc").WithLocation(5, 14), + // (5,28): error CS0246: The type or namespace name 'x' could not be found (are you missing a using directive or an assembly reference?) + // void LocalFunc(ref x) { }; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "x").WithArguments("x").WithLocation(5, 28), + // (5,29): error CS1001: Identifier expected + // void LocalFunc(ref x) { }; + Diagnostic(ErrorCode.ERR_IdentifierExpected, ")").WithLocation(5, 29)); + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var symbol = semanticModel.GetDeclaredSymbol(lambda)!; + + Assert.Equal(MethodKind.LocalFunction, symbol.MethodKind); + Assert.True(symbol.Parameters is [{ Name: "", Type: IErrorTypeSymbol { Name: "x" }, RefKind: RefKind.Ref, IsOptional: false }]); + } + + [Fact] + public void TestOverloadResolution1() + { + var compilation = CreateCompilation(""" + delegate void D(ref int x); + delegate void E(ref int x); + + class C + { + void M() + { + M1((ref x) => { }); + } + + void M1(D d) { } + void M1(E e) { } + } + """).VerifyDiagnostics( + // (8,9): error CS0121: The call is ambiguous between the following methods or properties: 'C.M1(D)' and 'C.M1(E)' + // M1((ref x) => { }); + Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments("C.M1(D)", "C.M1(E)").WithLocation(8, 9)); + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol!; + + Assert.Equal(MethodKind.LambdaMethod, symbol.MethodKind); + Assert.True(symbol.Parameters is [{ Name: "x", Type.SpecialType: SpecialType.System_Int32, RefKind: RefKind.Ref, IsOptional: false }]); + } + + [Theory, CombinatorialData] + public void TestOverloadResolution2(bool passByRef) + { + var compilation = CompileAndVerify($$""" + using System; + + delegate void D(ref int x); + delegate void E(int x); + + class C + { + static void Main() + { + M1(({{(passByRef ? "ref" : "")}} x) => { }); + } + + static void M1(D d) { Console.WriteLine(0); } + static void M1(E e) { Console.WriteLine(1); } + } + """, + expectedOutput: passByRef ? "0" : "1").VerifyDiagnostics().Compilation; + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol!; + + Assert.Equal(MethodKind.LambdaMethod, symbol.MethodKind); + Assert.True(symbol.Parameters is [{ Name: "x", Type.SpecialType: SpecialType.System_Int32, IsOptional: false } parameter] && + parameter.RefKind == (passByRef ? RefKind.Ref : RefKind.None)); + } + + [Fact] + public void TestTypeInference() + { + var compilation = CreateCompilation(""" + delegate void D(ref T x); + + class C + { + void M() + { + M1((ref x) => { }, ""); + } + + void M1(D d, T value) { } + } + """).VerifyDiagnostics(); + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol!; + + Assert.Equal(MethodKind.LambdaMethod, symbol.MethodKind); + Assert.True(symbol.Parameters is [{ Name: "x", Type.SpecialType: SpecialType.System_String, RefKind: RefKind.Ref, IsOptional: false }]); + } + + [Fact] + public void TestInModifier() + { + var compilation = CreateCompilation(""" + delegate void D(in int x); + + class C + { + void M() + { + D d = (in x) => + { + }; + } + } + """).VerifyDiagnostics(); + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol!; + + Assert.Equal(MethodKind.LambdaMethod, symbol.MethodKind); + Assert.True(symbol.Parameters is [{ Name: "x", Type.SpecialType: SpecialType.System_Int32, RefKind: RefKind.In, IsOptional: false }]); + } + + [Fact] + public void TestInModifierWriteWithinLambda() + { + var compilation = CreateCompilation(""" + delegate void D(in int x); + + class C + { + void M() + { + D d = (in x) => + { + x = 1; + }; + } + } + """).VerifyDiagnostics( + // (9,13): error CS8331: Cannot assign to variable 'x' or use it as the right hand side of a ref assignment because it is a readonly variable + // x = 1; + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "x").WithArguments("variable", "x").WithLocation(9, 13)); + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol!; + + Assert.Equal(MethodKind.LambdaMethod, symbol.MethodKind); + Assert.True(symbol.Parameters is [{ Name: "x", Type.SpecialType: SpecialType.System_Int32, RefKind: RefKind.In, IsOptional: false }]); + } + + [Fact] + public void TestOutModifier() + { + var compilation = CreateCompilation(""" + delegate void D(out int x); + + class C + { + void M() + { + D d = (out x) => + { + x = 1; + }; + } + } + """).VerifyDiagnostics(); + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol!; + + Assert.Equal(MethodKind.LambdaMethod, symbol.MethodKind); + Assert.True(symbol.Parameters is [{ Name: "x", Type.SpecialType: SpecialType.System_Int32, RefKind: RefKind.Out, IsOptional: false }]); + } + + [Fact] + public void TestOutModifierMustBeWrittenWithinLambda() + { + var compilation = CreateCompilation(""" + delegate void D(out int x); + + class C + { + void M() + { + D d = (out x) => { }; + } + } + """).VerifyDiagnostics( + // (7,26): error CS0177: The out parameter 'x' must be assigned to before control leaves the current method + // D d = (out x) => { }; + Diagnostic(ErrorCode.ERR_ParamUnassigned, "{ }").WithArguments("x").WithLocation(7, 26)); + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol!; + + Assert.Equal(MethodKind.LambdaMethod, symbol.MethodKind); + Assert.True(symbol.Parameters is [{ Name: "x", Type.SpecialType: SpecialType.System_Int32, RefKind: RefKind.Out, IsOptional: false }]); + } + + [Theory, CombinatorialData] + public void TestExpressionTree(bool explicitType) + { + var compilation = CreateCompilation($$""" + using System.Linq.Expressions; + + delegate int D(ref int x); + + class C + { + void M() + { + Expression e = (ref {{(explicitType ? "int " : "")}}x) => 0; + } + } + """).VerifyDiagnostics( + // (9,33): error CS1951: An expression tree lambda may not contain a ref, in or out parameter + // Expression e = (ref x) => 0; + Diagnostic(ErrorCode.ERR_ByRefParameterInExpressionTree, "x")); + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol!; + + Assert.Equal(MethodKind.LambdaMethod, symbol.MethodKind); + Assert.True(symbol.Parameters is [{ Name: "x", Type.SpecialType: SpecialType.System_Int32, RefKind: RefKind.Ref, IsOptional: false }]); + } + + [Fact] + public void TestNullable1() + { + var compilation = CreateCompilation($$""" + #nullable enable + + delegate void D(ref string? x); + + class C + { + void M() + { + D d = (ref x) => + { + string y = x; + }; + } + } + """).VerifyDiagnostics( + // (11,24): warning CS8600: Converting null literal or possible null value to non-nullable type. + // string y = x; + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "x").WithLocation(11, 24)); + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol!; + + Assert.Equal(MethodKind.LambdaMethod, symbol.MethodKind); + Assert.True(symbol.Parameters is [{ Name: "x", Type.SpecialType: SpecialType.System_String, Type.NullableAnnotation: CodeAnalysis.NullableAnnotation.Annotated, RefKind: RefKind.Ref, IsOptional: false }]); + } + + [Fact] + public void TestNullable2() + { + var compilation = CreateCompilation($$""" + #nullable enable + + delegate void D(ref string x); + + class C + { + void M() + { + D d = (ref x) => + { + x = null; + }; + } + } + """).VerifyDiagnostics( + // (11,17): warning CS8625: Cannot convert null literal to non-nullable reference type. + // x = null; + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(11, 17)); + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol!; + + Assert.Equal(MethodKind.LambdaMethod, symbol.MethodKind); + Assert.True(symbol.Parameters is [{ Name: "x", Type.SpecialType: SpecialType.System_String, Type.NullableAnnotation: CodeAnalysis.NullableAnnotation.NotAnnotated, RefKind: RefKind.Ref, IsOptional: false }]); + } + + [Fact] + public void TestDynamic() + { + var compilation = CreateCompilation($$""" + delegate void D(ref dynamic x); + + class C + { + void M() + { + D d = (ref x) => + { + x = null; + x = 1; + x = ""; + x.NonExistent(); + }; + } + } + """).VerifyDiagnostics(); + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol!; + + Assert.Equal(MethodKind.LambdaMethod, symbol.MethodKind); + Assert.True(symbol.Parameters is [{ Name: "x", Type.TypeKind: TypeKind.Dynamic, RefKind: RefKind.Ref, IsOptional: false }]); + } + + [Theory, CombinatorialData] + public void TestParams1(bool delegateIsParams) + { + var compilation = CreateCompilation($$""" + delegate void D({{(delegateIsParams ? "params" : "")}} int[] x); + + class C + { + void M() + { + D d = (params x) => + { + }; + } + } + """); + + if (delegateIsParams) + { + compilation.VerifyDiagnostics( + // (7,16): error CS9272: Implicitly typed lambda parameter 'x' cannot have the 'params' modifier. + // D d = (params x) => + Diagnostic(ErrorCode.ERR_ImplicitlyTypedParamsParameter, "params").WithArguments("x").WithLocation(7, 16)); + } + else + { + compilation.VerifyDiagnostics( + // (7,16): error CS9272: Implicitly typed lambda parameter 'x' cannot have the 'params' modifier. + // D d = (params x) => + Diagnostic(ErrorCode.ERR_ImplicitlyTypedParamsParameter, "params").WithArguments("x").WithLocation(7, 16), + // (7,23): warning CS9100: Parameter 1 has params modifier in lambda but not in target delegate type. + // D d = (params x) => + Diagnostic(ErrorCode.WRN_ParamsArrayInLambdaOnly, "x").WithArguments("1").WithLocation(7, 23)); + } + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol!; + + Assert.Equal(MethodKind.LambdaMethod, symbol.MethodKind); + Assert.True(symbol.Parameters is [{ Name: "x", Type: IArrayTypeSymbol { ElementType.SpecialType: SpecialType.System_Int32 }, IsParams: true }]); + } + + [Fact] + public void TestParams2() + { + var compilation = CreateCompilation($$""" + delegate void D(params int[] x); + + class C + { + void M() + { + D d = (x) => + { + }; + } + } + """).VerifyDiagnostics(); + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol!; + + Assert.Equal(MethodKind.LambdaMethod, symbol.MethodKind); + Assert.True(symbol.Parameters is [{ Name: "x", Type: IArrayTypeSymbol { ElementType.SpecialType: SpecialType.System_Int32 }, IsParams: false }]); + } + [Fact] + public void TestIOperation() + { + var compilation = CreateCompilation(""" + delegate void D(ref int x); + + class C + { + void M() + { + D d = (ref x) => { return; }; + } + } + """).VerifyDiagnostics(); + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var operation = (IAnonymousFunctionOperation)semanticModel.GetOperation(lambda)!; + Assert.NotNull(operation); + + compilation.VerifyOperationTree(lambda, """ + IAnonymousFunctionOperation (Symbol: lambda expression) (OperationKind.AnonymousFunction, Type: null) (Syntax: '(ref x) => { return; }') + IBlockOperation (1 statements) (OperationKind.Block, Type: null) (Syntax: '{ return; }') + IReturnOperation (OperationKind.Return, Type: null) (Syntax: 'return;') + ReturnedValue: + null + """); + + var symbol = operation.Symbol; + + Assert.Equal(MethodKind.LambdaMethod, symbol.MethodKind); + Assert.Equal(RefKind.Ref, symbol.Parameters.Single().RefKind); + Assert.Equal(SpecialType.System_Int32, symbol.Parameters.Single().Type.SpecialType); + + Assert.NotNull(operation.Body); + Assert.Single(operation.Body.Operations); + Assert.True(operation.Body.Operations.Single() is IReturnOperation { ReturnedValue: null }); + } + + [Fact] + public void TestParamsRef() + { + var compilation = CreateCompilation(""" + delegate void D(ref int[] x); + + class C + { + void M() + { + D d = (params ref x) => { }; + } + } + """).VerifyDiagnostics( + // (7,16): error CS9272: Implicitly typed lambda parameter 'x' cannot have the 'params' modifier. + // D d = (params ref x) => { }; + Diagnostic(ErrorCode.ERR_ImplicitlyTypedParamsParameter, "params").WithArguments("x").WithLocation(7, 16), + // (7,23): error CS1611: The params parameter cannot be declared as ref + // D d = (params ref x) => { }; + Diagnostic(ErrorCode.ERR_ParamsCantBeWithModifier, "ref").WithArguments("ref").WithLocation(7, 23), + // (7,27): warning CS9100: Parameter 1 has params modifier in lambda but not in target delegate type. + // D d = (params ref x) => { }; + Diagnostic(ErrorCode.WRN_ParamsArrayInLambdaOnly, "x").WithArguments("1").WithLocation(7, 27)); + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol!; + + Assert.Equal(MethodKind.LambdaMethod, symbol.MethodKind); + Assert.True(symbol.Parameters is [{ Name: "x", Type: IArrayTypeSymbol { ElementType.SpecialType: SpecialType.System_Int32 }, RefKind: RefKind.Ref, IsParams: true }]); + } + + [Fact] + public void TestRefParams() + { + var compilation = CreateCompilation(""" + delegate void D(ref int[] x); + + class C + { + void M() + { + D d = (ref params x) => { }; + } + } + """).VerifyDiagnostics( + // (7,20): error CS8328: The parameter modifier 'params' cannot be used with 'ref' + // D d = (ref params x) => { }; + Diagnostic(ErrorCode.ERR_BadParameterModifiers, "params").WithArguments("params", "ref").WithLocation(7, 20), + // (7,20): error CS9272: Implicitly typed lambda parameter 'x' cannot have the 'params' modifier. + // D d = (ref params x) => { }; + Diagnostic(ErrorCode.ERR_ImplicitlyTypedParamsParameter, "params").WithArguments("x").WithLocation(7, 20), + // (7,27): warning CS9100: Parameter 1 has params modifier in lambda but not in target delegate type. + // D d = (ref params x) => { }; + Diagnostic(ErrorCode.WRN_ParamsArrayInLambdaOnly, "x").WithArguments("1").WithLocation(7, 27)); + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol!; + + Assert.Equal(MethodKind.LambdaMethod, symbol.MethodKind); + Assert.True(symbol.Parameters is [{ Name: "x", Type: IArrayTypeSymbol { ElementType.SpecialType: SpecialType.System_Int32 }, RefKind: RefKind.Ref, IsParams: true }]); + } + + [Fact] + public void TestRefToOut() + { + var compilation = CreateCompilation(""" + delegate void D(ref int x); + + class C + { + void M() + { + D d = (out x) => { x = 1; }; + } + } + """).VerifyDiagnostics( + // (7,20): error CS1676: Parameter 1 must be declared with the 'ref' keyword + // D d = (out x) => { x = 1; }; + Diagnostic(ErrorCode.ERR_BadParamRef, "x").WithArguments("1", "ref").WithLocation(7, 20)); + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol!; + + Assert.Equal(MethodKind.LambdaMethod, symbol.MethodKind); + Assert.True(symbol.Parameters is [{ Name: "x", Type: IErrorTypeSymbol, RefKind: RefKind.Out, IsParams: false }]); + } + + [Fact] + public void TestOutToRef() + { + var compilation = CreateCompilation(""" + delegate void D(out int x); + + class C + { + void M() + { + D d = (ref x) => { x = 1; }; + } + } + """).VerifyDiagnostics( + // (7,20): error CS1676: Parameter 1 must be declared with the 'out' keyword + // D d = (ref x) => { x = 1; }; + Diagnostic(ErrorCode.ERR_BadParamRef, "x").WithArguments("1", "out").WithLocation(7, 20)); + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol!; + + Assert.Equal(MethodKind.LambdaMethod, symbol.MethodKind); + Assert.True(symbol.Parameters is [{ Name: "x", Type: IErrorTypeSymbol, RefKind: RefKind.Ref, IsParams: false }]); + } + + [Theory, CombinatorialData] + public void TestScopedOnDelegateNotOnLambda(bool includeType) + { + var compilation = CreateCompilationWithSpan($$""" + using System; + delegate void D(scoped ReadOnlySpan x); + + class C + { + void M() + { + D d = ({{(includeType ? "ReadOnlySpan " : "")}}x) => { }; + } + } + """).VerifyDiagnostics(); + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol!; + + Assert.Equal(MethodKind.LambdaMethod, symbol.MethodKind); + Assert.Equal(ScopedKind.None, symbol.Parameters.Single().ScopedKind); + Assert.Equal("x", symbol.Parameters.Single().Name); + Assert.Equal(compilation.GetTypeByMetadataName(typeof(ReadOnlySpan<>).FullName).GetPublicSymbol(), symbol.Parameters.Single().Type.OriginalDefinition); + } + + [Theory, CombinatorialData] + public void TestScopedOnLambdaNotOnDelegate(bool includeType) + { + var compilation = CreateCompilationWithSpan($$""" + using System; + delegate void D(ReadOnlySpan x); + + class C + { + void M() + { + D d = (scoped {{(includeType ? "ReadOnlySpan " : "")}}x) => { }; + } + } + """).VerifyDiagnostics(); + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol!; + + Assert.Equal(MethodKind.LambdaMethod, symbol.MethodKind); + Assert.Equal(ScopedKind.ScopedValue, symbol.Parameters.Single().ScopedKind); + Assert.Equal("x", symbol.Parameters.Single().Name); + Assert.Equal(compilation.GetTypeByMetadataName(typeof(ReadOnlySpan<>).FullName).GetPublicSymbol(), symbol.Parameters.Single().Type.OriginalDefinition); + } + + [Fact] + public void TestParamsOnNonParamsType() + { + var compilation = CreateCompilation(""" + delegate void D(int x); + + class C + { + void M() + { + D d = (params x) => { }; + } + } + """).VerifyDiagnostics( + // (7,16): error CS9272: Implicitly typed lambda parameter 'x' cannot have the 'params' modifier. + // D d = (params x) => { }; + Diagnostic(ErrorCode.ERR_ImplicitlyTypedParamsParameter, "params").WithArguments("x").WithLocation(7, 16), + // (7,16): error CS0225: The params parameter must have a valid collection type + // D d = (params x) => { }; + Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(7, 16)); + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol!; + + Assert.Equal(MethodKind.LambdaMethod, symbol.MethodKind); + Assert.True(symbol.Parameters is [{ Name: "x", Type.SpecialType: SpecialType.System_Int32, IsParams: true }]); + } + + [Fact] + public void TestAccessibilityModifier1() + { + var compilation = CreateCompilation(""" + delegate void D(ref int x); + + class C + { + void M() + { + D d = (public ref x) => { }; + } + } + """).VerifyDiagnostics( + // (7,16): error CS1525: Invalid expression term 'public' + // D d = (public ref x) => { }; + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "public").WithArguments("public").WithLocation(7, 16), + // (7,16): error CS1026: ) expected + // D d = (public ref x) => { }; + Diagnostic(ErrorCode.ERR_CloseParenExpected, "public").WithLocation(7, 16), + // (7,16): error CS1002: ; expected + // D d = (public ref x) => { }; + Diagnostic(ErrorCode.ERR_SemicolonExpected, "public").WithLocation(7, 16), + // (7,16): error CS1513: } expected + // D d = (public ref x) => { }; + Diagnostic(ErrorCode.ERR_RbraceExpected, "public").WithLocation(7, 16), + // (7,28): error CS1519: Invalid token ')' in class, record, struct, or interface member declaration + // D d = (public ref x) => { }; + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, ")").WithArguments(")").WithLocation(7, 28), + // (7,28): error CS1519: Invalid token ')' in class, record, struct, or interface member declaration + // D d = (public ref x) => { }; + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, ")").WithArguments(")").WithLocation(7, 28), + // (9,1): error CS1022: Type or namespace definition, or end-of-file expected + // } + Diagnostic(ErrorCode.ERR_EOFExpected, "}").WithLocation(9, 1)); + } + + [Fact] + public void TestAccessibilityModifier2() + { + var compilation = CreateCompilation(""" + delegate void D(ref int x); + + class C + { + void M() + { + D d = (ref public x) => { }; + } + } + """).VerifyDiagnostics( + // (7,16): error CS1525: Invalid expression term 'ref' + // D d = (ref public x) => { }; + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "ref ").WithArguments("ref").WithLocation(7, 16), + // (7,16): error CS1073: Unexpected token 'ref' + // D d = (ref public x) => { }; + Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(7, 16), + // (7,20): error CS1525: Invalid expression term 'public' + // D d = (ref public x) => { }; + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "public").WithArguments("public").WithLocation(7, 20), + // (7,20): error CS1026: ) expected + // D d = (ref public x) => { }; + Diagnostic(ErrorCode.ERR_CloseParenExpected, "public").WithLocation(7, 20), + // (7,20): error CS1002: ; expected + // D d = (ref public x) => { }; + Diagnostic(ErrorCode.ERR_SemicolonExpected, "public").WithLocation(7, 20), + // (7,20): error CS1513: } expected + // D d = (ref public x) => { }; + Diagnostic(ErrorCode.ERR_RbraceExpected, "public").WithLocation(7, 20), + // (7,28): error CS1519: Invalid token ')' in class, record, struct, or interface member declaration + // D d = (ref public x) => { }; + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, ")").WithArguments(")").WithLocation(7, 28), + // (7,28): error CS1519: Invalid token ')' in class, record, struct, or interface member declaration + // D d = (ref public x) => { }; + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, ")").WithArguments(")").WithLocation(7, 28), + // (9,1): error CS1022: Type or namespace definition, or end-of-file expected + // } + Diagnostic(ErrorCode.ERR_EOFExpected, "}").WithLocation(9, 1)); + } + + [Fact] + public void TestOneParameterWithReadonlyRef() + { + var compilation = CreateCompilation(""" + delegate void D(ref int x); + + class C + { + void M() + { + D d = (readonly ref x) => { }; + } + } + """).VerifyDiagnostics( + // (7,16): error CS9190: 'readonly' modifier must be specified after 'ref'. + // D d = (readonly ref x) => { }; + Diagnostic(ErrorCode.ERR_RefReadOnlyWrongOrdering, "readonly").WithLocation(7, 16)); + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol!; + + Assert.Equal(MethodKind.LambdaMethod, symbol.MethodKind); + Assert.Equal(RefKind.Ref, symbol.Parameters.Single().RefKind); + Assert.Equal(SpecialType.System_Int32, symbol.Parameters.Single().Type.SpecialType); + } + + [Fact] + public void TestOneParameterWithRefReadonly1() + { + var compilation = CreateCompilation(""" + delegate void D(ref int x); + + class C + { + void M() + { + D d = (ref readonly x) => { }; + } + } + """).VerifyDiagnostics( + // (7,15): warning CS9198: Reference kind modifier of parameter 'ref readonly int x' doesn't match the corresponding parameter 'ref int x' in target. + // D d = (ref readonly x) => { }; + Diagnostic(ErrorCode.WRN_TargetDifferentRefness, "(ref readonly x) => { }").WithArguments("ref readonly int x", "ref int x").WithLocation(7, 15)); + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol!; + + Assert.Equal(MethodKind.LambdaMethod, symbol.MethodKind); + Assert.Equal(RefKind.RefReadOnlyParameter, symbol.Parameters.Single().RefKind); + Assert.Equal(SpecialType.System_Int32, symbol.Parameters.Single().Type.SpecialType); + } + + [Fact] + public void TestOneParameterWithRefReadonly2() + { + var compilation = CreateCompilation(""" + delegate void D(ref readonly int x); + + class C + { + void M() + { + D d = (ref readonly x) => { }; + } + } + """).VerifyDiagnostics(); + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol!; + + Assert.Equal(MethodKind.LambdaMethod, symbol.MethodKind); + Assert.Equal(RefKind.RefReadOnlyParameter, symbol.Parameters.Single().RefKind); + Assert.Equal(SpecialType.System_Int32, symbol.Parameters.Single().Type.SpecialType); + } + + [Fact] + public void TestOneParameterWithRefReadonly3() + { + var compilation = CreateCompilation(""" + delegate void D(ref readonly int x); + + class C + { + void M() + { + D d = (ref readonly x) => + { + x = 0; + }; + } + } + """).VerifyDiagnostics( + // (9,13): error CS8331: Cannot assign to variable 'x' or use it as the right hand side of a ref assignment because it is a readonly variable + // x = 0; + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "x").WithArguments("variable", "x").WithLocation(9, 13)); + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol!; + + Assert.Equal(MethodKind.LambdaMethod, symbol.MethodKind); + Assert.Equal(RefKind.RefReadOnlyParameter, symbol.Parameters.Single().RefKind); + Assert.Equal(SpecialType.System_Int32, symbol.Parameters.Single().Type.SpecialType); + } + + [Fact] + public void TestOneParameterWithRefReadonly4() + { + var compilation = CreateCompilation(""" + delegate void D(ref readonly int x); + + class C + { + void M() + { + D d = (ref x) => + { + x = 0; + }; + } + } + """).VerifyDiagnostics( + // (7,20): error CS1676: Parameter 1 must be declared with the 'ref readonly' keyword + // D d = (ref x) => + Diagnostic(ErrorCode.ERR_BadParamRef, "x").WithArguments("1", "ref readonly").WithLocation(7, 20)); + + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + var lambda = root.DescendantNodes().OfType().Single(); + + var semanticModel = compilation.GetSemanticModel(tree); + var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(lambda).Symbol!; + + Assert.Equal(MethodKind.LambdaMethod, symbol.MethodKind); + Assert.Equal(RefKind.Ref, symbol.Parameters.Single().RefKind); + Assert.Equal(SpecialType.None, symbol.Parameters.Single().Type.SpecialType); + } +} diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/NullablePublicAPITests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/NullablePublicAPITests.cs index 8a6b68ec48a89..0aa044a022b6e 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/NullablePublicAPITests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/NullablePublicAPITests.cs @@ -3961,15 +3961,12 @@ public static void M(object? o) var comp = CreateCompilation(source, options: WithNullableEnable()); comp.VerifyDiagnostics( - // (12,29): error CS0748: Inconsistent lambda parameter usage; parameter types must be all explicit or all implicit - // Create(o1, (o2, object o3, object? o4) => { }); - Diagnostic(ErrorCode.ERR_InconsistentLambdaParameterUsage, "object").WithLocation(12, 29), - // (12,40): error CS0748: Inconsistent lambda parameter usage; parameter types must be all explicit or all implicit - // Create(o1, (o2, object o3, object? o4) => { }); - Diagnostic(ErrorCode.ERR_InconsistentLambdaParameterUsage, "object?").WithLocation(12, 40), - // (13,48): error CS0748: Inconsistent lambda parameter usage; parameter types must be all explicit or all implicit - // Create(o1, (object o2, object? o3, o4) => { }); - Diagnostic(ErrorCode.ERR_InconsistentLambdaParameterUsage, "o4").WithLocation(13, 48)); + // (12,25): error CS0748: Inconsistent lambda parameter usage; parameter types must be all explicit or all implicit + // Create(o1, (o2, object o3, object? o4) => { }); + Diagnostic(ErrorCode.ERR_InconsistentLambdaParameterUsage, "o2").WithLocation(12, 25), + // (13,48): error CS0748: Inconsistent lambda parameter usage; parameter types must be all explicit or all implicit + // Create(o1, (object o2, object? o3, o4) => { }); + Diagnostic(ErrorCode.ERR_InconsistentLambdaParameterUsage, "o4").WithLocation(13, 48)); var syntaxTree = comp.SyntaxTrees[0]; var root = syntaxTree.GetRoot(); diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationScopeParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationScopeParsingTests.cs index 5632b71b1ed5c..6740c40c0c393 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationScopeParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationScopeParsingTests.cs @@ -1162,29 +1162,41 @@ public void Lambda_06() { string source = "(ref scoped scoped R r) => { }"; UsingExpression(source, TestOptions.Regular11, - // (1,1): error CS1073: Unexpected token 'scoped' - // (ref scoped scoped R r) => { } - Diagnostic(ErrorCode.ERR_UnexpectedToken, "(ref scoped ").WithArguments("scoped").WithLocation(1, 1), - // (1,2): error CS1525: Invalid expression term 'ref' - // (ref scoped scoped R r) => { } - Diagnostic(ErrorCode.ERR_InvalidExprTerm, "ref scoped").WithArguments("ref").WithLocation(1, 2), - // (1,13): error CS1026: ) expected + // (1,20): error CS1003: Syntax error, ',' expected // (ref scoped scoped R r) => { } - Diagnostic(ErrorCode.ERR_CloseParenExpected, "scoped").WithLocation(1, 13) - ); + Diagnostic(ErrorCode.ERR_SyntaxError, "R").WithArguments(",").WithLocation(1, 20)); - N(SyntaxKind.ParenthesizedExpression); + N(SyntaxKind.ParenthesizedLambdaExpression); { - N(SyntaxKind.OpenParenToken); - N(SyntaxKind.RefExpression); + N(SyntaxKind.ParameterList); { - N(SyntaxKind.RefKeyword); - N(SyntaxKind.IdentifierName); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } N(SyntaxKind.IdentifierToken, "scoped"); } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "R"); + } + N(SyntaxKind.IdentifierToken, "r"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); } - M(SyntaxKind.CloseParenToken); } EOF(); } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/LambdaAttributeParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/LambdaAttributeParsingTests.cs index fe1b2715e0110..50984cae0d14a 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/LambdaAttributeParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/LambdaAttributeParsingTests.cs @@ -233,10 +233,7 @@ void verify() public void LambdaAttribute_05() { string source = "[A] (ref x) => x"; - UsingExpression(source, TestOptions.RegularPreview, - // (1,11): error CS1001: Identifier expected - // [A] (ref x) => x - Diagnostic(ErrorCode.ERR_IdentifierExpected, ")").WithLocation(1, 11)); + UsingExpression(source, TestOptions.RegularPreview); N(SyntaxKind.ParenthesizedLambdaExpression); { @@ -258,11 +255,7 @@ public void LambdaAttribute_05() N(SyntaxKind.Parameter); { N(SyntaxKind.RefKeyword); - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "x"); - } - M(SyntaxKind.IdentifierToken); + N(SyntaxKind.IdentifierToken, "x"); } N(SyntaxKind.CloseParenToken); } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/LambdaParameterParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/LambdaParameterParsingTests.cs index 6a715e65d95ac..ec020c8c12ef8 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/LambdaParameterParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/LambdaParameterParsingTests.cs @@ -2,27 +2,22 @@ // 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 Microsoft.CodeAnalysis.CSharp.Test.Utilities; -using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; using Xunit.Abstractions; namespace Microsoft.CodeAnalysis.CSharp.UnitTests { - public class LambdaParameterParsingTests : ParsingTests + public sealed class LambdaParameterParsingTests(ITestOutputHelper output) + : ParsingTests(output) { - public LambdaParameterParsingTests(ITestOutputHelper output) : base(output) { } - - protected override SyntaxTree ParseTree(string text, CSharpParseOptions options) + protected override SyntaxTree ParseTree(string text, CSharpParseOptions? options) { return SyntaxFactory.ParseSyntaxTree(text, options: options); } - protected override CSharpSyntaxNode ParseNode(string text, CSharpParseOptions options) + protected override CSharpSyntaxNode ParseNode(string text, CSharpParseOptions? options) { return SyntaxFactory.ParseExpression(text, options: options); } @@ -5686,11 +5681,11 @@ public void ScopedAsParameterName_05() EOF(); } - [Fact, WorkItem(63469, "https://github.com/dotnet/roslyn/issues/63469")] - public void ScopedAsParameterName_06() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/63469")] + public void ScopedAsParameterName_06_CSharp13() { string source = "(scoped scoped) => { }"; - UsingExpression(source); + UsingExpression(source, TestOptions.Regular13); N(SyntaxKind.ParenthesizedLambdaExpression); { @@ -5717,6 +5712,34 @@ public void ScopedAsParameterName_06() EOF(); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/63469")] + public void ScopedAsParameterName_06_Preview() + { + string source = "(scoped scoped) => { }"; + UsingExpression(source, TestOptions.RegularPreview); + + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.IdentifierToken, "scoped"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + [Fact] public void MissingParameterName_01() { @@ -5827,6 +5850,855 @@ public void MissingParameterName_03() EOF(); } - } + [Fact] + public void TestParameterModifierNoType1() + { + string source = "(ref a) => { }"; + UsingExpression(source); + + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void TestParameterModifierNoType1_a() + { + string source = "(a, ref b) => { }"; + UsingExpression(source); + + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierToken, "b"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void TestParameterModifierNoType2() + { + string source = "(ref readonly a) => { }"; + UsingExpression(source); + + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.ReadOnlyKeyword); + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void TestParameterModifierNoType2_a() + { + string source = "(a, ref readonly b) => { }"; + UsingExpression(source); + + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.ReadOnlyKeyword); + N(SyntaxKind.IdentifierToken, "b"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void TestParameterModifierNoType3() + { + string source = "(readonly ref a) => { }"; + UsingExpression(source); + + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.ReadOnlyKeyword); + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void TestParameterModifierNoType3_A() + { + string source = "(a, readonly ref b) => { }"; + UsingExpression(source); + + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.ReadOnlyKeyword); + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierToken, "b"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void TestParameterModifierNoType4() + { + string source = "(scoped a) => { }"; + UsingExpression(source, TestOptions.RegularPreview); + + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void TestParameterModifierNoType4_a() + { + string source = "(a, scoped b) => { }"; + UsingExpression(source, TestOptions.RegularPreview); + + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.IdentifierToken, "b"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + [Fact] + public void TestParameterModifierNoType4_CSharp13() + { + string source = "(scoped a) => { }"; + UsingExpression(source, TestOptions.Regular13); + + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void TestParameterModifierNoType4_a_CSharp13() + { + string source = "(a, scoped b) => { }"; + UsingExpression(source, TestOptions.Regular13); + + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + N(SyntaxKind.IdentifierToken, "b"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void TestParameterModifierNoType5() + { + string source = "(scoped out a) => { }"; + UsingExpression(source); + + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.OutKeyword); + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void TestParameterModifierNoType5_a() + { + string source = "(a, scoped out b) => { }"; + UsingExpression(source); + + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.OutKeyword); + N(SyntaxKind.IdentifierToken, "b"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void TestParameterModifierNoType6() + { + string source = "(ref a = 1) => { }"; + UsingExpression(source); + + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierToken, "a"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void TestParameterModifierNoType6_a() + { + string source = "(a, ref b = 1) => { }"; + UsingExpression(source); + + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierToken, "b"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void TestParameterModifierNoType7() + { + string source = "([Attr] ref a) => { }"; + UsingExpression(source); + + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.AttributeList); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Attribute); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Attr"); + } + } + N(SyntaxKind.CloseBracketToken); + } + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void TestParameterModifierNoType7_a() + { + string source = "(a, [Attr] ref a) => { }"; + UsingExpression(source); + + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.AttributeList); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Attribute); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Attr"); + } + } + N(SyntaxKind.CloseBracketToken); + } + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void TestParameterModifierNoType8() + { + string source = "(scoped ref readonly a) => { }"; + UsingExpression(source); + + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.RefKeyword); + N(SyntaxKind.ReadOnlyKeyword); + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void TestParameterModifierNoType8_a() + { + string source = "(a, scoped ref readonly b) => { }"; + UsingExpression(source); + + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.RefKeyword); + N(SyntaxKind.ReadOnlyKeyword); + N(SyntaxKind.IdentifierToken, "b"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void TestParameterModifierNoType9_CSharp13() + { + // 'scoped' continues to be a type in C#13 and below. + string source = "(scoped a = null) => { }"; + UsingExpression(source, TestOptions.Regular13); + + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + N(SyntaxKind.IdentifierToken, "a"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NullLiteralExpression); + { + N(SyntaxKind.NullKeyword); + } + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void TestParameterModifierNoType9_CSharp14() + { + // 'scoped' is always a modifier in C# 14 and above. + string source = "(scoped a = null) => { }"; + UsingExpression(source, TestOptions.RegularNext); + + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.IdentifierToken, "a"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NullLiteralExpression); + { + N(SyntaxKind.NullKeyword); + } + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void TestParameterModifierNoType10() + { + string source = "([Attr] ref a = 0) => { }"; + UsingExpression(source); + + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.AttributeList); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Attribute); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Attr"); + } + } + N(SyntaxKind.CloseBracketToken); + } + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierToken, "a"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void TestOptionalImplicitParameter() + { + string source = "(a = 0) => { }"; + UsingExpression(source); + + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierToken, "a"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void TestAttributedImplicitParameter() + { + string source = "([Attr] a) => { }"; + UsingExpression(source); + + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.AttributeList); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Attribute); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Attr"); + } + } + N(SyntaxKind.CloseBracketToken); + } + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void TestAttributedImplicitParameterWithInitializer() + { + string source = "([Attr] a = 0) => { }"; + UsingExpression(source); + + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.AttributeList); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Attribute); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Attr"); + } + } + N(SyntaxKind.CloseBracketToken); + } + N(SyntaxKind.IdentifierToken, "a"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + } } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/LambdaReturnTypeParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/LambdaReturnTypeParsingTests.cs index e7d0e088e2699..ddaefdc18abb9 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/LambdaReturnTypeParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/LambdaReturnTypeParsingTests.cs @@ -4102,16 +4102,7 @@ public void InvocationOrLambda_10() public void InvocationOrLambda_11() { string source = "F(ref a, out b, in c) => { }"; - UsingExpression(source, - // (1,8): error CS1001: Identifier expected - // F(ref a, out b, in c) => { } - Diagnostic(ErrorCode.ERR_IdentifierExpected, ",").WithLocation(1, 8), - // (1,15): error CS1001: Identifier expected - // F(ref a, out b, in c) => { } - Diagnostic(ErrorCode.ERR_IdentifierExpected, ",").WithLocation(1, 15), - // (1,21): error CS1001: Identifier expected - // F(ref a, out b, in c) => { } - Diagnostic(ErrorCode.ERR_IdentifierExpected, ")").WithLocation(1, 21)); + UsingExpression(source); N(SyntaxKind.ParenthesizedLambdaExpression); { @@ -4125,31 +4116,19 @@ public void InvocationOrLambda_11() N(SyntaxKind.Parameter); { N(SyntaxKind.RefKeyword); - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "a"); - } - M(SyntaxKind.IdentifierToken); + N(SyntaxKind.IdentifierToken, "a"); } N(SyntaxKind.CommaToken); N(SyntaxKind.Parameter); { N(SyntaxKind.OutKeyword); - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "b"); - } - M(SyntaxKind.IdentifierToken); + N(SyntaxKind.IdentifierToken, "b"); } N(SyntaxKind.CommaToken); N(SyntaxKind.Parameter); { N(SyntaxKind.InKeyword); - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "c"); - } - M(SyntaxKind.IdentifierToken); + N(SyntaxKind.IdentifierToken, "c"); } N(SyntaxKind.CloseParenToken); } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/ParserErrorMessageTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/ParserErrorMessageTests.cs index be08d997f4c18..ed9f8aeb45c92 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/ParserErrorMessageTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/ParserErrorMessageTests.cs @@ -1309,27 +1309,27 @@ static void X() "; CreateCompilation(test).VerifyDiagnostics( - // (10,41): error CS0748: Inconsistent lambda parameter usage; parameter types must be all explicit or all implicit - // Func f1 = (int x, y) => 1; // err: mixed parameters - Diagnostic(ErrorCode.ERR_InconsistentLambdaParameterUsage, "y"), - // (11,37): error CS0748: Inconsistent lambda parameter usage; parameter types must be all explicit or all implicit - // Func f2 = (x, int y) => 1; // err: mixed parameters - Diagnostic(ErrorCode.ERR_InconsistentLambdaParameterUsage, "int"), - // (12,48): error CS0748: Inconsistent lambda parameter usage; parameter types must be all explicit or all implicit - // Func f3 = (int x, int y, z) => 1; // err: mixed parameters - Diagnostic(ErrorCode.ERR_InconsistentLambdaParameterUsage, "z"), - // (13,41): error CS0748: Inconsistent lambda parameter usage; parameter types must be all explicit or all implicit - // Func f4 = (int x, y, int z) => 1; // err: mixed parameters - Diagnostic(ErrorCode.ERR_InconsistentLambdaParameterUsage, "y"), - // (14,37): error CS0748: Inconsistent lambda parameter usage; parameter types must be all explicit or all implicit - // Func f5 = (x, int y, int z) => 1; // err: mixed parameters - Diagnostic(ErrorCode.ERR_InconsistentLambdaParameterUsage, "int"), + // (11,44): error CS0748: Inconsistent lambda parameter usage; parameter types must be all explicit or all implicit + // Func f1 = (int x, y) => 1; // err: mixed parameters + Diagnostic(ErrorCode.ERR_InconsistentLambdaParameterUsage, "y").WithLocation(11, 44), + // (12,37): error CS0748: Inconsistent lambda parameter usage; parameter types must be all explicit or all implicit + // Func f2 = (x, int y) => 1; // err: mixed parameters + Diagnostic(ErrorCode.ERR_InconsistentLambdaParameterUsage, "x").WithLocation(12, 37), + // (13,51): error CS0748: Inconsistent lambda parameter usage; parameter types must be all explicit or all implicit + // Func f3 = (int x, int y, z) => 1; // err: mixed parameters + Diagnostic(ErrorCode.ERR_InconsistentLambdaParameterUsage, "z").WithLocation(13, 51), // (14,44): error CS0748: Inconsistent lambda parameter usage; parameter types must be all explicit or all implicit - // Func f5 = (x, int y, int z) => 1; // err: mixed parameters - Diagnostic(ErrorCode.ERR_InconsistentLambdaParameterUsage, "int"), - // (15,40): error CS0748: Inconsistent lambda parameter usage; parameter types must be all explicit or all implicit - // Func f6 = (x, y, int z) => 1; // err: mixed parameters - Diagnostic(ErrorCode.ERR_InconsistentLambdaParameterUsage, "int")); + // Func f4 = (int x, y, int z) => 1; // err: mixed parameters + Diagnostic(ErrorCode.ERR_InconsistentLambdaParameterUsage, "y").WithLocation(14, 44), + // (15,37): error CS0748: Inconsistent lambda parameter usage; parameter types must be all explicit or all implicit + // Func f5 = (x, int y, int z) => 1; // err: mixed parameters + Diagnostic(ErrorCode.ERR_InconsistentLambdaParameterUsage, "x").WithLocation(15, 37), + // (16,37): error CS0748: Inconsistent lambda parameter usage; parameter types must be all explicit or all implicit + // Func f6 = (x, y, int z) => 1; // err: mixed parameters + Diagnostic(ErrorCode.ERR_InconsistentLambdaParameterUsage, "x").WithLocation(16, 37), + // (16,40): error CS0748: Inconsistent lambda parameter usage; parameter types must be all explicit or all implicit + // Func f6 = (x, y, int z) => 1; // err: mixed parameters + Diagnostic(ErrorCode.ERR_InconsistentLambdaParameterUsage, "y").WithLocation(16, 40)); } [WorkItem(535915, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/535915")]