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,