diff --git a/src/EditorFeatures/CSharpTest/AddParameter/AddParameterTests.cs b/src/EditorFeatures/CSharpTest/AddParameter/AddParameterTests.cs index ad38881cf7f95d6e5517a09063fc51c105bc5afb..683e989559ca9aae8266ee4827624925e1d4a944 100644 --- a/src/EditorFeatures/CSharpTest/AddParameter/AddParameterTests.cs +++ b/src/EditorFeatures/CSharpTest/AddParameter/AddParameterTests.cs @@ -181,6 +181,57 @@ void M() { new C(1, true); } +}"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddParameter)] + public async Task TestParams1() + { + await TestAsync( +@" +class C +{ + public C(params int[] i) { } +} + +class D +{ + void M() + { + new C([|true|], 1); + } +}", +@" +class C +{ + public C(bool v, params int[] i) { } +} + +class D +{ + void M() + { + new C(true, 1); + } +}"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddParameter)] + public async Task TestParams2() + { + await TestMissingAsync( +@" +class C +{ + public C(params int[] i) { } +} + +class D +{ + void M() + { + new [|C|](1, true); + } }"); } } diff --git a/src/Features/Core/Portable/AddParameter/AbstractAddParameterCodeFixProvider.cs b/src/Features/Core/Portable/AddParameter/AbstractAddParameterCodeFixProvider.cs index c81876ab6c0dfb715c399ba300e1f58b4cbdc1cb..e6718112135f9f7a82a65c7b9b81204143c2629a 100644 --- a/src/Features/Core/Portable/AddParameter/AbstractAddParameterCodeFixProvider.cs +++ b/src/Features/Core/Portable/AddParameter/AbstractAddParameterCodeFixProvider.cs @@ -91,7 +91,7 @@ private Task HandleInvocationExpressionAsync(CodeFixContext context, TInvocation foreach (var constructor in type.InstanceConstructors.OrderBy(m => m.Parameters.Length)) { if (IsInSource(constructor) && - constructor.Parameters.Length < arguments.Count) + NonParamsParameterCount(constructor) < arguments.Count) { var argumentToAdd = DetermineFirstArgumentToAdd( semanticModel, syntaxFacts, comparer, constructor, arguments); @@ -111,6 +111,9 @@ private Task HandleInvocationExpressionAsync(CodeFixContext context, TInvocation } } + private int NonParamsParameterCount(IMethodSymbol method) + => method.IsParams() ? method.Parameters.Length - 1 : method.Parameters.Length; + private bool IsInSource(ISymbol symbol) => !symbol.IsImplicitlyDeclared && symbol.DeclaringSyntaxReferences.Length > 0 && @@ -188,11 +191,11 @@ private static SyntaxNode GetNewMethodDeclaration(IMethodSymbol method, TArgumen miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes); private TArgumentSyntax DetermineFirstArgumentToAdd( - SemanticModel semanticModel, - ISyntaxFactsService syntaxFacts, - StringComparer comparer, - IMethodSymbol method, - SeparatedSyntaxList arguments) + SemanticModel semanticModel, + ISyntaxFactsService syntaxFacts, + StringComparer comparer, + IMethodSymbol method, + SeparatedSyntaxList arguments) { var methodParameterNames = new HashSet(comparer); methodParameterNames.AddRange(method.Parameters.Select(p => p.Name)); @@ -217,17 +220,28 @@ private static SyntaxNode GetNewMethodDeclaration(IMethodSymbol method, TArgumen // then this definitely is an argument we could add. if (i >= method.Parameters.Length) { + if (method.Parameters.LastOrDefault()?.IsParams == true) + { + // Last parameter is a params. We can't place any parameters past it. + return null; + } + return argument; } var argumentTypeInfo = semanticModel.GetTypeInfo(syntaxFacts.GetExpressionOfArgument(argument)); var parameter = method.Parameters[i]; - // If this argument already matches the method's parameter, then we don't want - // to add it. - if (!parameter.Type.Equals(argumentTypeInfo.Type) && - !parameter.Type.Equals(argumentTypeInfo.ConvertedType)) + if (!TypeInfoMatchesType(argumentTypeInfo, parameter.Type)) { + if (parameter.IsParams && parameter.Type is IArrayTypeSymbol arrayType) + { + if (TypeInfoMatchesType(argumentTypeInfo, arrayType.ElementType)) + { + return null; + } + } + return argument; } } @@ -236,6 +250,9 @@ private static SyntaxNode GetNewMethodDeclaration(IMethodSymbol method, TArgumen return null; } + private bool TypeInfoMatchesType(TypeInfo argumentTypeInfo, ITypeSymbol type) + => type.Equals(argumentTypeInfo.Type) || type.Equals(argumentTypeInfo.ConvertedType); + private class MyCodeAction : CodeAction.DocumentChangeAction { public MyCodeAction(