提交 b696b896 编写于 作者: G Gen Lu

Add support for FixAll

上级 aac69302
......@@ -406,6 +406,62 @@ static int AddLocal(int @static)
}",
parseOptions: CSharp8ParseOptions);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeLocalFunctionStatic)]
public async Task TestFixAll()
{
await TestInRegularAndScriptAsync(
@"class C
{
int M(int x)
{
int y = 10;
var m = AddLocal(1, 2);
return AddLocal(m, m);
static int AddLocal(int a, int b)
{
return a + b + x + y;
}
}
int N(int x)
{
int y = 10;
return AddLocal(AddLocal(1, 2), AddLocal(3, 4));
static int AddLocal(int a, int b)
{
return AddLocal(a, b) + {|FixAllInDocument:|}x + y;
}
}
}",
@"class C
{
int M(int x)
{
int y = 10;
var m = AddLocal(1, 2, x, y);
return AddLocal(m, m, x, y);
static int AddLocal(int a, int b, int x, int y)
{
return a + b + x + y;
}
}
int N(int x)
{
int y = 10;
return AddLocal(AddLocal(1, 2, x, y), AddLocal(3, 4, x, y), x, y);
static int AddLocal(int a, int b, int x, int y)
{
return AddLocal(a, b, x, y) + x + y;
}
}
}", parseOptions: CSharp8ParseOptions);
}
}
}
......@@ -41,6 +41,20 @@ public static bool CanMakeLocalFunctionStatic(ImmutableArray<ISymbol> captures)
LocalFunctionStatementSyntax localFunction,
ImmutableArray<ISymbol> captures,
CancellationToken cancellationToken)
{
var root = (await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false))!;
var syntaxEditor = new SyntaxEditor(root, document.Project.Solution.Workspace);
await MakeLocalFunctionStaticAsync(document, semanticModel, localFunction, captures, syntaxEditor, cancellationToken).ConfigureAwait(false);
return document.WithSyntaxRoot(syntaxEditor.GetChangedRoot());
}
public static async Task MakeLocalFunctionStaticAsync(
Document document,
SemanticModel semanticModel,
LocalFunctionStatementSyntax localFunction,
ImmutableArray<ISymbol> captures,
SyntaxEditor syntaxEditor,
CancellationToken cancellationToken)
{
var root = (await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false))!;
var localFunctionSymbol = semanticModel.GetDeclaredSymbol(localFunction, cancellationToken);
......@@ -52,7 +66,7 @@ public static bool CanMakeLocalFunctionStatic(ImmutableArray<ISymbol> captures)
// Now we need to find all the refereces to the local function that we might need to fix.
var shouldWarn = false;
using var builderDisposer = ArrayBuilder<InvocationExpressionSyntax>.GetInstance(out var builder);
using var builderDisposer = ArrayBuilder<InvocationExpressionSyntax>.GetInstance(out var invocations);
foreach (var referencedSymbol in referencedSymbols)
{
......@@ -70,7 +84,7 @@ public static bool CanMakeLocalFunctionStatic(ImmutableArray<ISymbol> captures)
if (identifierNode.Parent is InvocationExpressionSyntax invocation)
{
builder.Add(invocation);
invocations.Add(invocation);
}
else
{
......@@ -82,10 +96,9 @@ public static bool CanMakeLocalFunctionStatic(ImmutableArray<ISymbol> captures)
}
var parameterAndCapturedSymbols = CreateParameterSymbols(captures);
var syntaxEditor = new SyntaxEditor(root, document.Project.Solution.Workspace);
// Fix all invocations by passing in additional arguments.
foreach (var invocation in builder)
foreach (var invocation in invocations)
{
syntaxEditor.ReplaceNode(
invocation,
......@@ -157,8 +170,6 @@ public static bool CanMakeLocalFunctionStatic(ImmutableArray<ISymbol> captures)
return AddStaticModifier(localFunctionWithNewParameters, CSharpSyntaxGenerator.Instance);
});
return document.WithSyntaxRoot(syntaxEditor.GetChangedRoot());
}
public static SyntaxNode AddStaticModifier(SyntaxNode localFunction, SyntaxGenerator generator)
......
......@@ -11,27 +11,62 @@
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Editing;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.MakeLocalFunctionStatic
{
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(PassInCapturedVariablesAsArgumentsCodeFixProvider)), Shared]
internal sealed class PassInCapturedVariablesAsArgumentsCodeFixProvider : CodeFixProvider
internal sealed class PassInCapturedVariablesAsArgumentsCodeFixProvider : SyntaxEditorBasedCodeFixProvider
{
// "CS8421: A static local function can't contain a reference to <variable>."
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create("CS8421");
public override FixAllProvider? GetFixAllProvider()
internal override CodeFixCategory CodeFixCategory => CodeFixCategory.Compile;
public override Task RegisterCodeFixesAsync(CodeFixContext context)
{
return null;
var diagnostic = context.Diagnostics.First();
return WrapFixAsync(
context.Document,
ImmutableArray.Create(diagnostic),
(document, semanticModel, localFunction, captures, cancellationToken) =>
{
context.RegisterCodeFix(
new MyCodeAction(c => MakeLocalFunctionStaticHelper.MakeLocalFunctionStaticAsync(
document,
semanticModel,
localFunction,
captures,
c)),
diagnostic);
return Task.CompletedTask;
},
context.CancellationToken);
}
public override async Task RegisterCodeFixesAsync(CodeFixContext context)
protected override Task FixAllAsync(Document document, ImmutableArray<Diagnostic> diagnostics, SyntaxEditor editor, CancellationToken cancellationToken)
=> WrapFixAsync(
document,
diagnostics,
(d, semanticModel, localFunction, captures, token) => MakeLocalFunctionStaticHelper.MakeLocalFunctionStaticAsync(
d,
semanticModel,
localFunction,
captures,
editor,
token),
cancellationToken);
private static async Task WrapFixAsync(
Document document,
ImmutableArray<Diagnostic> diagnostics,
Func<Document, SemanticModel, LocalFunctionStatementSyntax, ImmutableArray<ISymbol>, CancellationToken, Task> fixer,
CancellationToken cancellationToken)
{
var diagnostic = context.Diagnostics.First();
var diagnosticSpan = diagnostic.Location.SourceSpan;
var root = (await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false))!;
var root = (await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false))!;
// Even when the language version doesn't support staic local function, the compiler will still
// generate this error. So we need to check to make sure we don't provide incorrect fix.
......@@ -40,22 +75,27 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
return;
}
var localFunction = root.FindNode(diagnosticSpan).AncestorsAndSelf().OfType<LocalFunctionStatementSyntax>().FirstOrDefault();
if (localFunction == null)
var localFunctions = diagnostics
.Select(d => root.FindNode(d.Location.SourceSpan).AncestorsAndSelf().OfType<LocalFunctionStatementSyntax>().FirstOrDefault())
.Where(n => n != null)
.Distinct()
.ToImmutableArrayOrEmpty();
if (localFunctions.Length == 0)
{
return;
}
var document = context.Document;
var cancellationToken = context.CancellationToken;
var semanticModel = (await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false))!;
if (MakeLocalFunctionStaticHelper.TryGetCaputuredSymbols(localFunction, semanticModel, out var captures) &&
MakeLocalFunctionStaticHelper.CanMakeLocalFunctionStatic(captures))
foreach (var localFunction in localFunctions)
{
context.RegisterCodeFix(
new MyCodeAction(c => MakeLocalFunctionStaticHelper.MakeLocalFunctionStaticAsync(document, semanticModel, localFunction, captures, c)),
diagnostic);
if (MakeLocalFunctionStaticHelper.TryGetCaputuredSymbols(localFunction, semanticModel, out var captures) &&
MakeLocalFunctionStaticHelper.CanMakeLocalFunctionStatic(captures))
{
await fixer(document, semanticModel, localFunction, captures, cancellationToken).ConfigureAwait(false);
}
}
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册