提交 e67a1b0a 编写于 作者: S Shyam N

Avoid duplicate generate method fixes in light bulb

Fixes #1899

Consider the following case where M1 and M2 are non-existant methods -

this.M1(System.Exception.M2());

The code fix provider for generate method was offering two fixes to generate the method M1 above. Here's why. The fix provider gets called once each for the two diagnostics on the above line - the first with span corresponding to M1 and the second with span corresponding to M2. For the first span, the fix provider correctly constructs a fix to generate M1.

For the second span the fix provider correctly realizes that it can't construct a fix to generate M2 since System.Exception is a type from metadata and not source. However, looks like fix provider has fallback code to walk up the tree and see if there are other methods that it can construct in such cases. It ends up finding the InvocationExpression for M1 up the tree and therfore ends up returning another fix for generating M1.

I didn't want to change this fall back logic since it appears to be shared across multiple GFU fix providers and I didn't want to break other legit cases where this may be required for generate method.

Instead I am fixing this by introducing equivalence key for the corresponding CodeAction to ensure that the duplicate fixes will get filtered out when the light bulb presents the list of fixes in its UI. (This is identical to equivalence keys that we have in place for other GFU fixes such as Generate Type etc.). Note that we need the equivalence key for generate method anyways to avoid duplicate fixes in other unrelated cases such as following where M is a non-existant method -

var x = this; var y = this;
string.Format("{0}:{1}", x.M(), y.M())

Also, note that in some cases where we used to offer multiple valid fixes to generate methods with the same name but different signatures before, we will now only display one fix at a time. But this is fine since user can always fix first issue then invoke light bulb again to fix 2nd issue. Example of a case where there are 2 methods with same name but different signature -

this.M(this.M());
上级 6e7ddc3f
......@@ -2,12 +2,8 @@
using System;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CodeGeneration;
using Microsoft.CodeAnalysis.CSharp.CodeFixes.GenerateMethod;
using Microsoft.CodeAnalysis.CSharp.Diagnostics;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Test.Utilities;
using Xunit;
......@@ -2767,6 +2763,15 @@ public void TestGenerateExplicitConversionTargetTypeNotInSource()
@"class Digit { public Digit ( double d ) { val = d ; } public double val ; } class Program { static void Main ( string [ ] args ) { Digit dig = new Digit ( 7 ) ; double num = [|( double ) dig|] ; } } ",
@"using System ; class Digit { public Digit ( double d ) { val = d ; } public double val ; public static explicit operator double ( Digit v ) { throw new NotImplementedException ( ) ; } } class Program { static void Main ( string [ ] args ) { Digit dig = new Digit ( 7 ) ; double num = ( double ) dig ; } } ");
}
[WorkItem(774321)]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateMethod)]
public void TestEquivalenceKey()
{
TestEquivalenceKey(
@"class C { void M() { this.[|M1|](System.Exception.M2()); } } ",
string.Format(FeaturesResources.GenerateMethodIn, "C", "M1"));
}
}
}
}
......@@ -198,6 +198,20 @@ protected FixAllScope GetFixAllScope(string annotation)
}
}
protected void TestEquivalenceKey(
string initialMarkup,
string equivalenceKey,
int index = 0,
ParseOptions parseOptions = null,
CompilationOptions compilationOptions = null)
{
using (var workspace = CreateWorkspaceFromFile(initialMarkup, parseOptions, compilationOptions))
{
var diagnosticAndFix = GetDiagnosticAndFix(workspace);
Assert.Equal(equivalenceKey, diagnosticAndFix.Item2.Fixes.ElementAt(index).Action.EquivalenceKey);
}
}
protected void TestExactActionSetOffered(
string initialMarkup,
IEnumerable<string> expectedActionSet,
......
......@@ -5,8 +5,6 @@
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeGeneration;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.GenerateMember.GenerateParameterizedMember
......@@ -20,6 +18,7 @@ private partial class GenerateParameterizedMemberCodeAction : CodeAction
private readonly State _state;
private readonly bool _isAbstract;
private readonly bool _generateProperty;
private readonly string _equivalenceKey;
public GenerateParameterizedMemberCodeAction(
TService service,
......@@ -33,6 +32,7 @@ private partial class GenerateParameterizedMemberCodeAction : CodeAction
_state = state;
_isAbstract = isAbstract;
_generateProperty = generateProperty;
_equivalenceKey = Title;
}
private string GetDisplayText(
......@@ -101,6 +101,14 @@ public override string Title
return GetDisplayText(_state, _isAbstract, _generateProperty);
}
}
public override string EquivalenceKey
{
get
{
return _equivalenceKey;
}
}
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册