diff --git a/src/EditorFeatures/CSharpTest/AddParameter/AddParameterTests.cs b/src/EditorFeatures/CSharpTest/AddParameter/AddParameterTests.cs index 942c7eaeba4751454349e6528be2b14e181bff65..8125cd00b0cb476d80cf1e3fe71ef834e0fde137 100644 --- a/src/EditorFeatures/CSharpTest/AddParameter/AddParameterTests.cs +++ b/src/EditorFeatures/CSharpTest/AddParameter/AddParameterTests.cs @@ -1696,5 +1696,43 @@ public class T "; await TestInRegularAndScriptAsync(code, fix); } + + [WorkItem(21446, "https://github.com/dotnet/roslyn/issues/21446")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddParameter)] + public async Task TestInvocation_Cascading_OfferFixCascadingForImplicitInterface() + { + // error CS1501: No overload for method 'M1' takes 1 arguments + var code = +@" + interface I1 + { + void M1(); + } + class C: I1 + { + public void M1() { } + void MTest() + { + [|M1|](1); + } + } +"; + var fix0 = +@" + interface I1 + { + void M1(int v); + } + class C: I1 + { + public void M1(int v) { } + void MTest() + { + M1(1); + } + } +"; + await TestInRegularAndScriptAsync(code, fix0); + } } } diff --git a/src/Features/Core/Portable/AddParameter/AbstractAddParameterCodeFixProvider.cs b/src/Features/Core/Portable/AddParameter/AbstractAddParameterCodeFixProvider.cs index 461e485155880e0aef87dc61219175cda2ddc0f0..44c3cd7a38e30275f616957b948ccc72c693b0f2 100644 --- a/src/Features/Core/Portable/AddParameter/AbstractAddParameterCodeFixProvider.cs +++ b/src/Features/Core/Portable/AddParameter/AbstractAddParameterCodeFixProvider.cs @@ -152,9 +152,8 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) var methodToUpdate = argumentInsertPositionData.MethodToUpdate; var argumentToInsert = argumentInsertPositionData.ArgumentToInsert; var parameters = methodToUpdate.Parameters.Select(p => p.ToDisplayString(SimpleFormat)); - var signature = $"{methodToUpdate.Name}({string.Join(", ", parameters)})"; - - var title = string.Format(FeaturesResources.Add_parameter_to_0, signature); + var title = GetCodeFixTitle(methodToUpdate, parameters); + var hasCascadingDeclarations = HasCascadingDeclarations(methodToUpdate); context.RegisterCodeFix( new MyCodeAction(title, c => FixAsync(context.Document, methodToUpdate, argumentToInsert, arguments, c)), @@ -162,6 +161,54 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) } } + /// + /// Checks if there are indications that there might be more than one declaration that needs to be fixed. + /// The check does not look-up if there are other declarations (this is done later in the CodeAction). + /// + /// + /// + private bool HasCascadingDeclarations(IMethodSymbol method) + { + // Virtual methods of all kinds might have overrides somewhere else that need to be fixed. + if (method.IsVirtual || method.IsOverride || method.IsAbstract) + { + return true; + } + + // If interfaces are involved we will fix those too + // Explicit interface implementations are easy + if (method.ExplicitInterfaceImplementations.Length > 0) + { + return true; + } + + // For implicit interface implementations lets check if the characteristic of the method + // allows it to implicit implement an interface member. + if (method.DeclaredAccessibility == Accessibility.Private || method.DeclaredAccessibility == Accessibility.NotApplicable) + { + return false; + } + + if (method.IsStatic) + { + return false; + } + + // Now check if the method does implement an interface member + var containingType = method.ContainingType; + var allMethodsInAllInterfaces = containingType.AllInterfaces.SelectMany(i => i.GetMembers(method.Name)); + var isMethodImplementingAnInterfaceMember = allMethodsInAllInterfaces.Any( + methodInInterface => containingType.FindImplementationForInterfaceMember(methodInInterface) == method); + return isMethodImplementingAnInterfaceMember; + } + + private static string GetCodeFixTitle(IMethodSymbol methodToUpdate, IEnumerable parameters) + { + var signature = $"{methodToUpdate.Name}({string.Join(", ", parameters)})"; + var title = string.Format(FeaturesResources.Add_parameter_to_0, signature); + return title; + } + private ImmutableArray> GetArgumentInsertPositionForMethodCandidates( TArgumentSyntax argumentOpt, SemanticModel semanticModel,