diff --git a/src/EditorFeatures/CSharpTest/AddParameter/AddParameterTests.cs b/src/EditorFeatures/CSharpTest/AddParameter/AddParameterTests.cs index 8d9c42ffdb219a94fe3cc6e339c7604a03ccc147..8afd3ff9c79537c705363201f115165bc88d0dc1 100644 --- a/src/EditorFeatures/CSharpTest/AddParameter/AddParameterTests.cs +++ b/src/EditorFeatures/CSharpTest/AddParameter/AddParameterTests.cs @@ -2001,8 +2001,6 @@ void Test() [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddParameter)] public async Task TestInvocation_InvocationStyles_Positional_WithOptionalParam() { - // The fix places new parameter after optional parameters (which is not allowed) - // I don't see what we can do about it without also changing the call site. // error CS1501: No overload for method 'M' takes 2 arguments var code = @" @@ -2014,12 +2012,11 @@ void Test() [|M|](1, 2); } }"; - // error CS1737: Optional parameters must appear after all required parameters var fix0 = @" class C { - void M(int i = 1, int v) { } + void M(int i = 1, int v = 0) { } void Test() { M(1, 2); diff --git a/src/Features/Core/Portable/AddParameter/AbstractAddParameterCodeFixProvider.cs b/src/Features/Core/Portable/AddParameter/AbstractAddParameterCodeFixProvider.cs index 184cce69b5f90b70a24bd8ded60e44f89897e533..c459cd2988da23b9e2ad377594b6ee24f13a5aea 100644 --- a/src/Features/Core/Portable/AddParameter/AbstractAddParameterCodeFixProvider.cs +++ b/src/Features/Core/Portable/AddParameter/AbstractAddParameterCodeFixProvider.cs @@ -326,10 +326,21 @@ private static string GetCodeFixTitle(string resourceString, IMethodSymbol metho foreach (var methodDeclaration in documentLookup) { var methodNode = syntaxRoot.FindNode(methodDeclaration.Locations[0].SourceSpan); + var existingParameters = generator.GetParameters(methodNode); + var insertionIndex = isNamedArgument + ? existingParameters.Count + : argumentList.IndexOf(argument); + + // if the preceding parameter is optional, the new parameter must also be optional + // see also BC30202 and CS1737 + var parameterMustBeOptional = insertionIndex > 0 && + syntaxFacts.GetDefaultOfParameter(existingParameters[insertionIndex - 1]) != null; + var parameterSymbol = CreateParameterSymbol( - methodDeclaration, argumentType, refKind, argumentNameSuggestion); + methodDeclaration, argumentType, refKind, parameterMustBeOptional, argumentNameSuggestion); - var parameterDeclaration = generator.ParameterDeclaration(parameterSymbol) + var argumentInitializer = parameterMustBeOptional ? generator.DefaultExpression(argumentType) : null; + var parameterDeclaration = generator.ParameterDeclaration(parameterSymbol, argumentInitializer) .WithAdditionalAnnotations(Formatter.Annotation); if (anySymbolReferencesNotInSource && methodDeclaration == method) { @@ -337,10 +348,6 @@ private static string GetCodeFixTitle(string resourceString, IMethodSymbol metho ConflictAnnotation.Create(FeaturesResources.Related_method_signatures_found_in_metadata_will_not_be_updated)); } - var existingParameters = generator.GetParameters(methodNode); - var insertionIndex = isNamedArgument - ? existingParameters.Count - : argumentList.IndexOf(argument); if (method.MethodKind == MethodKind.ReducedExtension) { @@ -409,11 +416,12 @@ private static async Task<(ITypeSymbol, RefKind)> GetArgumentTypeAndRefKindAsync IMethodSymbol method, ITypeSymbol parameterType, RefKind refKind, + bool isOptional, string argumentNameSuggestion) { var uniqueName = NameGenerator.EnsureUniqueness(argumentNameSuggestion, method.Parameters.Select(p => p.Name)); var newParameterSymbol = CodeGenerationSymbolFactory.CreateParameterSymbol( - attributes: default, refKind: refKind, isParams: false, type: parameterType, name: uniqueName); + attributes: default, refKind: refKind, isOptional: isOptional, isParams: false, type: parameterType, name: uniqueName); return newParameterSymbol; }