未验证 提交 3776f4d2 编写于 作者: G Gen Lu 提交者: GitHub

Merge pull request #37629 from lavanyaguru/MakeFuntionStatic

Codefix and Code Refactoring for making local functions static
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.MakeLocalFunctionStatic;
using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeRefactorings;
using Microsoft.CodeAnalysis.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.MakeLocalFunctionStatic
{
public class MakeLocalFunctionStaticRefactoringTests : AbstractCSharpCodeActionTest
{
protected override CodeRefactoringProvider CreateCodeRefactoringProvider(Workspace workspace, TestParameters parameters)
=> new MakeLocalFunctionStaticCodeRefactoringProvider();
private static ParseOptions CSharp72ParseOptions = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp7_2);
private static ParseOptions CSharp8ParseOptions = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp8);
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeLocalFunctionStatic)]
public async Task ShouldNotTriggerForCSharp7()
{
await TestMissingAsync(
@"class C
{
int N(int x)
{
return AddLocal();
int [||]AddLocal()
{
return x + 1;
}
}
}", parameters: new TestParameters(parseOptions: CSharp72ParseOptions));
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeLocalFunctionStatic)]
public async Task ShouldNotTriggerIfNoCaptures()
{
await TestMissingAsync(
@"class C
{
int N(int x)
{
return AddLocal(x);
int [||]AddLocal(int x)
{
return x + 1;
}
}
}", parameters: new TestParameters(parseOptions: CSharp8ParseOptions));
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeLocalFunctionStatic)]
public async Task ShouldNotTriggerIfAlreadyStatic()
{
await TestMissingAsync(
@"class C
{
int N(int x)
{
return AddLocal(x);
static int [||]AddLocal(int x)
{
return x + 1;
}
}
}", parameters: new TestParameters(parseOptions: CSharp8ParseOptions));
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeLocalFunctionStatic)]
public async Task ShouldNotTriggerIfAlreadyStaticWithError()
{
await TestMissingAsync(
@"class C
{
int N(int x)
{
return AddLocal();
static int [||]AddLocal()
{
return x + 1;
}
}
}", parameters: new TestParameters(parseOptions: CSharp8ParseOptions));
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeLocalFunctionStatic)]
public async Task ShouldNotTriggerIfCapturesThisParameter()
{
await TestMissingAsync(
@"class C
{
int x;
int N()
{
return AddLocal();
int [||]AddLocal()
{
return x + 1;
}
}
}", parameters: new TestParameters(parseOptions: CSharp8ParseOptions));
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeLocalFunctionStatic)]
public async Task ShouldTriggerIfExplicitlyPassedInThisParameter()
{
await TestInRegularAndScriptAsync(
@"class C
{
int x;
int N()
{
int y;
return AddLocal(this);
int [||]AddLocal(C c)
{
return c.x + y;
}
}
}",
@"class C
{
int x;
int N()
{
int y;
return AddLocal(this, y);
static int [||]AddLocal(C c, int y)
{
return c.x + y;
}
}
}", parseOptions: CSharp8ParseOptions);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeLocalFunctionStatic)]
public async Task ShouldTriggerForCSharp8()
{
await TestInRegularAndScriptAsync(
@"class C
{
int N(int x)
{
return AddLocal();
int [||]AddLocal()
{
return x + 1;
}
}
}",
@"class C
{
int N(int x)
{
return AddLocal(x);
static int AddLocal(int x)
{
return x + 1;
}
}
}",
parseOptions: CSharp8ParseOptions);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeLocalFunctionStatic)]
public async Task TestMultipleVariables()
{
await TestInRegularAndScriptAsync(
@"class C
{
int N(int x)
{
int y = 10;
return AddLocal();
int[||] AddLocal()
{
return x + y;
}
}
}",
@"class C
{
int N(int x)
{
int y = 10;
return AddLocal(x, y);
static int AddLocal(int x, int y)
{
return x + y;
}
}
}", parseOptions: CSharp8ParseOptions);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeLocalFunctionStatic)]
public async Task TestMultipleCalls()
{
await TestInRegularAndScriptAsync(
@"class C
{
int N(int x)
{
int y = 10;
return AddLocal() + AddLocal();
int[||] AddLocal()
{
return x + y;
}
}
}",
@"class C
{
int N(int x)
{
int y = 10;
return AddLocal(x, y) + AddLocal(x, y);
static int AddLocal(int x, int y)
{
return x + y;
}
}
}"
, parseOptions: CSharp8ParseOptions);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeLocalFunctionStatic)]
public async Task TestMultipleCallsWithExistingParameters()
{
await TestInRegularAndScriptAsync(
@"class C
{
int N(int x)
{
int y = 10;
var m = AddLocal(1, 2);
return AddLocal(m, m);
int[||] AddLocal(int a, int b)
{
return a + b + x + y;
}
}
}",
@"class C
{
int N(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;
}
}
}"
, parseOptions: CSharp8ParseOptions);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeLocalFunctionStatic)]
public async Task TestRecursiveCall()
{
await TestInRegularAndScriptAsync(
@"class C
{
int N(int x)
{
int y = 10;
var m = AddLocal(1, 2);
return AddLocal(m, m);
int[||] AddLocal(int a, int b)
{
return AddLocal(a, b) + x + y;
}
}
}",
@"class C
{
int N(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 AddLocal(a, b, x, y) + x + y;
}
}
}", parseOptions: CSharp8ParseOptions);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeLocalFunctionStatic)]
public async Task TestCallInArgumentList()
{
await TestInRegularAndScriptAsync(
@"class C
{
int N(int x)
{
int y = 10;
return AddLocal(AddLocal(1, 2), AddLocal(3, 4));
int[||] AddLocal(int a, int b)
{
return AddLocal(a, b) + x + y;
}
}
}",
@"class C
{
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);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeLocalFunctionStatic)]
public async Task TestCallsWithNamedArguments()
{
await TestInRegularAndScriptAsync(
@"class C
{
int N(int x)
{
int y = 10;
var m = AddLocal(1, b: 2);
return AddLocal(b: m, a: m);
int[||] AddLocal(int a, int b)
{
return a + b + x + y;
}
}
}",
@"class C
{
int N(int x)
{
int y = 10;
var m = AddLocal(1, b: 2, x: x, y: y);
return AddLocal(b: m, a: m, x: x, y: y);
static int AddLocal(int a, int b, int x, int y)
{
return a + b + x + y;
}
}
}"
, parseOptions: CSharp8ParseOptions);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeLocalFunctionStatic)]
public async Task TestCallsWithDafaultValue()
{
await TestInRegularAndScriptAsync(
@"class C
{
int N(int x)
{
string y = "";
var m = AddLocal(1);
return AddLocal(b: m);
int[||] AddLocal(int a = 0, int b = 0)
{
return a + b + x + y.Length;
}
}
}",
@"class C
{
int N(int x)
{
string y = "";
var m = AddLocal(1, x: x, y: y);
return AddLocal(b: m, x: x, y: y);
static int AddLocal(int a = 0, int b = 0, int x = 0, string y = null)
{
return a + b + x + y.Length;
}
}
}"
, parseOptions: CSharp8ParseOptions);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeLocalFunctionStatic)]
public async Task TestWarningAnnotation()
{
await TestInRegularAndScriptAsync(
@"class C
{
void N(int x)
{
Func<int> del = AddLocal;
int [||]AddLocal()
{
return x + 1;
}
}
}",
@"class C
{
void N(int x)
{
Func<int> del = AddLocal;
{|Warning:static int AddLocal(int x)
{
return x + 1;
}|}
}
}",
parseOptions: CSharp8ParseOptions);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeLocalFunctionStatic)]
public async Task TestNonCamelCaseCapture()
{
await TestInRegularAndScriptAsync(
@"class C
{
int N(int x)
{
int Static = 0;
return AddLocal();
int [||]AddLocal()
{
return Static + 1;
}
}
}",
@"class C
{
int N(int x)
{
int Static = 0;
return AddLocal(Static);
static int AddLocal(int @static)
{
return @static + 1;
}
}
}",
parseOptions: CSharp8ParseOptions);
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.MakeLocalFunctionStatic;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics;
using Microsoft.CodeAnalysis.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.MakeLocalFunctionStatic
{
public class PassInCapturedVariablesAsArgumentsCodeFixProviderTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest
{
internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace)
=> (null, new PassInCapturedVariablesAsArgumentsCodeFixProvider());
private static ParseOptions CSharp72ParseOptions = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp7_2);
private static ParseOptions CSharp8ParseOptions = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp8);
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseSimpleUsingStatement)]
public async Task TestMissingInCSharp7()
{
await TestMissingAsync(
@"class C
{
int N(int x)
{
return AddLocal();
static int AddLocal()
{
return [||]x + 1;
}
}
}", parameters: new TestParameters(parseOptions: CSharp72ParseOptions));
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseSimpleUsingStatement)]
public async Task TestMissingIfNoDiagnostic()
{
await TestMissingAsync(
@"class C
{
int N(int x)
{
return AddLocal();
int AddLocal()
{
return [||]x + 1;
}
}
}", parameters: new TestParameters(parseOptions: CSharp8ParseOptions));
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseSimpleUsingStatement)]
public async Task TestMissingIfCapturesThisParameter()
{
await TestMissingAsync(
@"class C
{
int y = 0;
int N(int x)
{
return AddLocal();
static int AddLocal()
{
return [||]x + y;
}
}
}", parameters: new TestParameters(parseOptions: CSharp8ParseOptions));
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeLocalFunctionStatic)]
public async Task ShouldTriggerForCSharp8()
{
await TestInRegularAndScriptAsync(
@"class C
{
int N(int x)
{
return AddLocal();
static int AddLocal()
{
return [||]x + 1;
}
}
}",
@"class C
{
int N(int x)
{
return AddLocal(x);
static int AddLocal(int x)
{
return x + 1;
}
}
}",
parseOptions: CSharp8ParseOptions);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeLocalFunctionStatic)]
public async Task TestMultipleVariables()
{
await TestInRegularAndScriptAsync(
@"class C
{
int N(int x)
{
int y = 10;
return AddLocal();
static int AddLocal()
{
return x + [||]y;
}
}
}",
@"class C
{
int N(int x)
{
int y = 10;
return AddLocal(x, y);
static int AddLocal(int x, int y)
{
return x + y;
}
}
}", parseOptions: CSharp8ParseOptions);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeLocalFunctionStatic)]
public async Task TestMultipleCalls()
{
await TestInRegularAndScriptAsync(
@"class C
{
int N(int x)
{
int y = 10;
return AddLocal() + AddLocal();
static int AddLocal()
{
return [||]x + y;
}
}
}",
@"class C
{
int N(int x)
{
int y = 10;
return AddLocal(x, y) + AddLocal(x, y);
static int AddLocal(int x, int y)
{
return x + y;
}
}
}"
, parseOptions: CSharp8ParseOptions);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeLocalFunctionStatic)]
public async Task TestMultipleCallsWithExistingParameters()
{
await TestInRegularAndScriptAsync(
@"class C
{
int N(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;
}
}
}",
@"class C
{
int N(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;
}
}
}"
, parseOptions: CSharp8ParseOptions);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeLocalFunctionStatic)]
public async Task TestRecursiveCall()
{
await TestInRegularAndScriptAsync(
@"class C
{
int N(int x)
{
int y = 10;
var m = AddLocal(1, 2);
return AddLocal(m, m);
static int AddLocal(int a, int b)
{
return AddLocal(a, b) + [||]x + y;
}
}
}",
@"class C
{
int N(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 AddLocal(a, b, x, y) + x + y;
}
}
}", parseOptions: CSharp8ParseOptions);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeLocalFunctionStatic)]
public async Task TestCallInArgumentList()
{
await TestInRegularAndScriptAsync(
@"class C
{
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) + [||]x + y;
}
}
}",
@"class C
{
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);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeLocalFunctionStatic)]
public async Task TestCallsWithNamedArguments()
{
await TestInRegularAndScriptAsync(
@"class C
{
int N(int x)
{
int y = 10;
var m = AddLocal(1, b: 2);
return AddLocal(b: m, a: m);
static int AddLocal(int a, int b)
{
return a + b + [||]x + y;
}
}
}",
@"class C
{
int N(int x)
{
int y = 10;
var m = AddLocal(1, b: 2, x: x, y: y);
return AddLocal(b: m, a: m, x: x, y: y);
static int AddLocal(int a, int b, int x, int y)
{
return a + b + x + y;
}
}
}"
, parseOptions: CSharp8ParseOptions);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeLocalFunctionStatic)]
public async Task TestCallsWithDafaultValue()
{
await TestInRegularAndScriptAsync(
@"class C
{
int N(int x)
{
string y = "";
var m = AddLocal(1);
return AddLocal(b: m);
static int AddLocal(int a = 0, int b = 0)
{
return a + b + x + [||]y.Length;
}
}
}",
@"class C
{
int N(int x)
{
string y = "";
var m = AddLocal(1, x: x, y: y);
return AddLocal(b: m, x: x, y: y);
static int AddLocal(int a = 0, int b = 0, int x = 0, string y = null)
{
return a + b + x + y.Length;
}
}
}"
, parseOptions: CSharp8ParseOptions);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeLocalFunctionStatic)]
public async Task TestWarningAnnotation()
{
await TestInRegularAndScriptAsync(
@"class C
{
void N(int x)
{
Func<int> del = AddLocal;
static int AddLocal()
{
return [||]x + 1;
}
}
}",
@"class C
{
void N(int x)
{
Func<int> del = AddLocal;
{|Warning:static int AddLocal(int x)
{
return x + 1;
}|}
}
}",
parseOptions: CSharp8ParseOptions);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeLocalFunctionStatic)]
public async Task TestNonCamelCaseCapture()
{
await TestInRegularAndScriptAsync(
@"class C
{
int N(int x)
{
int Static = 0;
return AddLocal();
static int AddLocal()
{
return [||]Static + 1;
}
}
}",
@"class C
{
int N(int x)
{
int Static = 0;
return AddLocal(Static);
static int AddLocal(int @static)
{
return @static + 1;
}
}
}",
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);
}
}
}
......@@ -1015,6 +1015,15 @@ internal class CSharpFeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to Pass in captured variables as arguments.
/// </summary>
internal static string Pass_in_captured_variables_as_arguments {
get {
return ResourceManager.GetString("Pass_in_captured_variables_as_arguments", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Properties.
/// </summary>
......@@ -1548,6 +1557,16 @@ internal class CSharpFeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to Warning: Adding parameters to local function declaration may produce invalid code..
/// </summary>
internal static string Warning_colon_Adding_parameters_to_local_function_declaration_may_produce_invalid_code {
get {
return ResourceManager.GetString("Warning_colon_Adding_parameters_to_local_function_declaration_may_produce_invalid" +
"_code", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Warning: Moving using directives may change code meaning..
/// </summary>
......
......@@ -669,4 +669,10 @@
<value>switch statement case clause</value>
<comment>{Locked="switch"}{Locked="case"} "switch" and "case" are a C# keyword and should not be localized.</comment>
</data>
<data name="Warning_colon_Adding_parameters_to_local_function_declaration_may_produce_invalid_code" xml:space="preserve">
<value>Warning: Adding parameters to local function declaration may produce invalid code.</value>
</data>
<data name="Pass_in_captured_variables_as_arguments" xml:space="preserve">
<value>Pass in captured variables as arguments</value>
</data>
</root>
\ No newline at end of file
......@@ -44,9 +44,7 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context)
{
editor.ReplaceNode(
localFunction,
(current, generator) => generator.WithModifiers(
current,
generator.GetModifiers(current).WithIsStatic(true)));
(current, generator) => MakeLocalFunctionStaticHelper.AddStaticModifier(current, generator));
}
return Task.CompletedTask;
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#nullable enable
using System;
using System.Composition;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Microsoft.CodeAnalysis.CSharp.MakeLocalFunctionStatic
{
[ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = nameof(MakeLocalFunctionStaticCodeRefactoringProvider)), Shared]
internal sealed class MakeLocalFunctionStaticCodeRefactoringProvider : CodeRefactoringProvider
{
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
{
var (document, textSpan, cancellationToken) = context;
var syntaxTree = (await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false))!;
if (!MakeLocalFunctionStaticHelper.IsStaticLocalFunctionSupported(syntaxTree))
{
return;
}
var localFunction = await context.TryGetRelevantNodeAsync<LocalFunctionStatementSyntax>().ConfigureAwait(false);
if (localFunction == null)
{
return;
}
if (localFunction.Modifiers.Any(SyntaxKind.StaticKeyword))
{
return;
}
var semanticModel = (await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false))!;
if (MakeLocalFunctionStaticHelper.TryGetCaputuredSymbolsAndCheckApplicability(localFunction, semanticModel, out var captures))
{
context.RegisterRefactoring(new MyCodeAction(
FeaturesResources.Make_local_function_static,
c => MakeLocalFunctionStaticHelper.MakeLocalFunctionStaticAsync(document, localFunction, captures, c)));
}
}
private class MyCodeAction : CodeAction.DocumentChangeAction
{
public MyCodeAction(string title, Func<CancellationToken, Task<Document>> createChangedDocument)
: base(title, createChangedDocument)
{
}
}
}
}
......@@ -35,8 +35,7 @@ private void AnalyzeSyntax(SyntaxNodeAnalysisContext context)
}
var syntaxTree = context.Node.SyntaxTree;
var options = (CSharpParseOptions)syntaxTree.Options;
if (options.LanguageVersion < LanguageVersion.CSharp8)
if (!MakeLocalFunctionStaticHelper.IsStaticLocalFunctionSupported(syntaxTree))
{
return;
}
......@@ -55,9 +54,7 @@ private void AnalyzeSyntax(SyntaxNodeAnalysisContext context)
}
var semanticModel = context.SemanticModel;
var analysis = semanticModel.AnalyzeDataFlow(localFunction);
var captures = analysis.CapturedInside;
if (analysis.Succeeded && captures.Length == 0)
if (MakeLocalFunctionStaticHelper.TryGetCaputuredSymbols(localFunction, semanticModel, out var captures) && captures.Length == 0)
{
context.ReportDiagnostic(DiagnosticHelper.Create(
Descriptor,
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#nullable enable
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeGeneration;
using Microsoft.CodeAnalysis.CSharp.CodeGeneration;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.MakeLocalFunctionStatic
{
internal static class MakeLocalFunctionStaticHelper
{
public static bool IsStaticLocalFunctionSupported(SyntaxTree tree)
=> tree.Options is CSharpParseOptions csharpOption && csharpOption.LanguageVersion >= LanguageVersion.CSharp8;
public static bool TryGetCaputuredSymbols(LocalFunctionStatementSyntax localFunction, SemanticModel semanticModel, out ImmutableArray<ISymbol> captures)
{
var dataFlow = semanticModel.AnalyzeDataFlow(localFunction);
captures = dataFlow.CapturedInside;
return dataFlow.Succeeded;
}
public static bool TryGetCaputuredSymbolsAndCheckApplicability(LocalFunctionStatementSyntax localFunction, SemanticModel semanticModel, out ImmutableArray<ISymbol> captures)
=> TryGetCaputuredSymbols(localFunction, semanticModel, out captures) && CanMakeLocalFunctionStatic(captures);
private static bool CanMakeLocalFunctionStatic(ImmutableArray<ISymbol> captures)
=> captures.Length > 0 && !captures.Any(s => s.IsThisParameter());
public static async Task<Document> MakeLocalFunctionStaticAsync(
Document document,
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, localFunction, captures, syntaxEditor, cancellationToken).ConfigureAwait(false);
return document.WithSyntaxRoot(syntaxEditor.GetChangedRoot());
}
public static async Task MakeLocalFunctionStaticAsync(
Document document,
LocalFunctionStatementSyntax localFunction,
ImmutableArray<ISymbol> captures,
SyntaxEditor syntaxEditor,
CancellationToken cancellationToken)
{
var root = (await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false))!;
var semanticModel = (await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false))!;
var localFunctionSymbol = semanticModel.GetDeclaredSymbol(localFunction, cancellationToken);
var documentImmutableSet = ImmutableHashSet.Create(document);
// Finds all the call sites of the local function
var referencedSymbols = await SymbolFinder.FindReferencesAsync(
localFunctionSymbol, document.Project.Solution, documentImmutableSet, cancellationToken).ConfigureAwait(false);
// 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 invocations);
foreach (var referencedSymbol in referencedSymbols)
{
foreach (var location in referencedSymbol.Locations)
{
// We limited the search scope to the single document,
// so all reference should be in the same tree.
var referenceNode = root.FindNode(location.Location.SourceSpan);
if (!(referenceNode is IdentifierNameSyntax identifierNode))
{
// Unexpected scenario, skip and warn.
shouldWarn = true;
continue;
}
if (identifierNode.Parent is InvocationExpressionSyntax invocation)
{
invocations.Add(invocation);
}
else
{
// We won't be able to fix non-invocation references,
// e.g. creating a delegate.
shouldWarn = true;
}
}
}
var parameterAndCapturedSymbols = CreateParameterSymbols(captures);
// Fix all invocations by passing in additional arguments.
foreach (var invocation in invocations)
{
syntaxEditor.ReplaceNode(
invocation,
(node, generator) =>
{
var currentInvocation = (InvocationExpressionSyntax)node;
var seenNamedArgument = currentInvocation.ArgumentList.Arguments.Any(a => a.NameColon != null);
var seenDefaultArgumentValue = currentInvocation.ArgumentList.Arguments.Count < localFunction.ParameterList.Parameters.Count;
var newArguments = parameterAndCapturedSymbols.Select(
p => generator.Argument(
seenNamedArgument || seenDefaultArgumentValue ? p.symbol.Name : null,
p.symbol.RefKind,
p.capture.Name.ToIdentifierName()) as ArgumentSyntax);
var newArgList = currentInvocation.ArgumentList.WithArguments(currentInvocation.ArgumentList.Arguments.AddRange(newArguments));
return currentInvocation.WithArgumentList(newArgList);
});
}
// In case any of the captured variable isn't camel-cased,
// we need to change the referenced name inside local function to use the new parameter's name.
foreach (var (parameter, capture) in parameterAndCapturedSymbols)
{
if (parameter.Name == capture.Name)
{
continue;
}
var referencedCaptureSymbols = await SymbolFinder.FindReferencesAsync(
capture, document.Project.Solution, documentImmutableSet, cancellationToken).ConfigureAwait(false);
foreach (var referencedSymbol in referencedCaptureSymbols)
{
foreach (var location in referencedSymbol.Locations)
{
var referenceSpan = location.Location.SourceSpan;
if (!localFunction.FullSpan.Contains(referenceSpan))
{
continue;
}
var referenceNode = root.FindNode(referenceSpan);
if (referenceNode is IdentifierNameSyntax identifierNode)
{
syntaxEditor.ReplaceNode(
identifierNode,
(node, generator) => generator.IdentifierName(parameter.Name.ToIdentifierToken()).WithTriviaFrom(node));
}
}
}
}
// Updates the local function declaration with variables passed in as parameters
syntaxEditor.ReplaceNode(
localFunction,
(node, generator) =>
{
var localFunctionWithNewParameters = CodeGenerator.AddParameterDeclarations(
node,
parameterAndCapturedSymbols.SelectAsArray(p => p.symbol),
document.Project.Solution.Workspace);
if (shouldWarn)
{
var annotation = WarningAnnotation.Create(CSharpFeaturesResources.Warning_colon_Adding_parameters_to_local_function_declaration_may_produce_invalid_code);
localFunctionWithNewParameters = localFunctionWithNewParameters.WithAdditionalAnnotations(annotation);
}
return AddStaticModifier(localFunctionWithNewParameters, CSharpSyntaxGenerator.Instance);
});
}
public static SyntaxNode AddStaticModifier(SyntaxNode localFunction, SyntaxGenerator generator)
=> generator.WithModifiers(
localFunction,
generator.GetModifiers(localFunction).WithIsStatic(true));
/// <summary>
/// Creates a new parameter symbol paired with the original captured symbol for each captured variables.
/// </summary>
private static ImmutableArray<(IParameterSymbol symbol, ISymbol capture)> CreateParameterSymbols(ImmutableArray<ISymbol> captures)
{
var parameters = ArrayBuilder<(IParameterSymbol, ISymbol)>.GetInstance(captures.Length);
foreach (var symbol in captures)
{
parameters.Add((CodeGenerationSymbolFactory.CreateParameterSymbol(
attributes: default,
refKind: RefKind.None,
isParams: false,
type: symbol.GetSymbolType(),
name: symbol.Name.ToCamelCase()), symbol));
}
return parameters.ToImmutableAndFree();
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#nullable enable
using System;
using System.Collections.Immutable;
using System.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.MakeLocalFunctionStatic
{
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(PassInCapturedVariablesAsArgumentsCodeFixProvider)), Shared]
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");
internal override CodeFixCategory CodeFixCategory => CodeFixCategory.Compile;
public override Task RegisterCodeFixesAsync(CodeFixContext context)
{
var diagnostic = context.Diagnostics.First();
return WrapFixAsync(
context.Document,
ImmutableArray.Create(diagnostic),
(document, localFunction, captures) =>
{
context.RegisterCodeFix(
new MyCodeAction(c => MakeLocalFunctionStaticHelper.MakeLocalFunctionStaticAsync(
document,
localFunction,
captures,
c)),
diagnostic);
return Task.CompletedTask;
},
context.CancellationToken);
}
protected override Task FixAllAsync(Document document, ImmutableArray<Diagnostic> diagnostics, SyntaxEditor editor, CancellationToken cancellationToken)
=> WrapFixAsync(
document,
diagnostics,
(d, localFunction, captures) => MakeLocalFunctionStaticHelper.MakeLocalFunctionStaticAsync(
d,
localFunction,
captures,
editor,
cancellationToken),
cancellationToken);
// The purpose of this wrapper is to share some common logic between FixOne and FixAll.
// The main reason we chose this approach over the typical "FixOne calls FixAll" approach is
// to avoid duplicate code.
private static async Task WrapFixAsync(
Document document,
ImmutableArray<Diagnostic> diagnostics,
Func<Document, LocalFunctionStatementSyntax, ImmutableArray<ISymbol>, Task> fixer,
CancellationToken cancellationToken)
{
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.
if (!MakeLocalFunctionStaticHelper.IsStaticLocalFunctionSupported(root.SyntaxTree))
{
return;
}
// Find all unique local functions that contain the error.
var localFunctions = diagnostics
.Select(d => root.FindNode(d.Location.SourceSpan).AncestorsAndSelf().OfType<LocalFunctionStatementSyntax>().FirstOrDefault())
.WhereNotNull()
.Distinct()
.ToImmutableArrayOrEmpty();
if (localFunctions.Length == 0)
{
return;
}
var semanticModel = (await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false))!;
foreach (var localFunction in localFunctions)
{
if (MakeLocalFunctionStaticHelper.TryGetCaputuredSymbolsAndCheckApplicability(localFunction, semanticModel, out var captures))
{
await fixer(document, localFunction, captures).ConfigureAwait(false);
}
}
}
private class MyCodeAction : CodeAction.DocumentChangeAction
{
public MyCodeAction(Func<CancellationToken, Task<Document>> createChangedDocument)
: base(CSharpFeaturesResources.Pass_in_captured_variables_as_arguments, createChangedDocument, CSharpFeaturesResources.Pass_in_captured_variables_as_arguments)
{
}
}
}
}
......@@ -112,6 +112,11 @@
<target state="translated">Nastavit jako ref struct</target>
<note>{Locked="ref"}{Locked="struct"} "ref" and "struct" are C# keywords and should not be localized.</note>
</trans-unit>
<trans-unit id="Pass_in_captured_variables_as_arguments">
<source>Pass in captured variables as arguments</source>
<target state="new">Pass in captured variables as arguments</target>
<note />
</trans-unit>
<trans-unit id="Remove_unnecessary_casts">
<source>Remove unnecessary casts</source>
<target state="translated">Odebrat nepotřebná přetypování</target>
......@@ -162,6 +167,11 @@
<target state="new">Warning: Inlining temporary into conditional method call.</target>
<note />
</trans-unit>
<trans-unit id="Warning_colon_Adding_parameters_to_local_function_declaration_may_produce_invalid_code">
<source>Warning: Adding parameters to local function declaration may produce invalid code.</source>
<target state="new">Warning: Adding parameters to local function declaration may produce invalid code.</target>
<note />
</trans-unit>
<trans-unit id="_0_is_not_null_here">
<source>'{0}' is not null here.</source>
<target state="new">'{0}' is not null here.</target>
......
......@@ -112,6 +112,11 @@
<target state="translated">"ref struct" erstellen</target>
<note>{Locked="ref"}{Locked="struct"} "ref" and "struct" are C# keywords and should not be localized.</note>
</trans-unit>
<trans-unit id="Pass_in_captured_variables_as_arguments">
<source>Pass in captured variables as arguments</source>
<target state="new">Pass in captured variables as arguments</target>
<note />
</trans-unit>
<trans-unit id="Remove_unnecessary_casts">
<source>Remove unnecessary casts</source>
<target state="translated">Nicht erforderliche Umwandlungen entfernen</target>
......@@ -162,6 +167,11 @@
<target state="new">Warning: Inlining temporary into conditional method call.</target>
<note />
</trans-unit>
<trans-unit id="Warning_colon_Adding_parameters_to_local_function_declaration_may_produce_invalid_code">
<source>Warning: Adding parameters to local function declaration may produce invalid code.</source>
<target state="new">Warning: Adding parameters to local function declaration may produce invalid code.</target>
<note />
</trans-unit>
<trans-unit id="_0_is_not_null_here">
<source>'{0}' is not null here.</source>
<target state="translated">"{0}" ist hier nicht NULL.</target>
......
......@@ -112,6 +112,11 @@
<target state="translated">Convertir "ref struct"</target>
<note>{Locked="ref"}{Locked="struct"} "ref" and "struct" are C# keywords and should not be localized.</note>
</trans-unit>
<trans-unit id="Pass_in_captured_variables_as_arguments">
<source>Pass in captured variables as arguments</source>
<target state="new">Pass in captured variables as arguments</target>
<note />
</trans-unit>
<trans-unit id="Remove_unnecessary_casts">
<source>Remove unnecessary casts</source>
<target state="translated">Quitar conversiones innecesarias</target>
......@@ -162,6 +167,11 @@
<target state="new">Warning: Inlining temporary into conditional method call.</target>
<note />
</trans-unit>
<trans-unit id="Warning_colon_Adding_parameters_to_local_function_declaration_may_produce_invalid_code">
<source>Warning: Adding parameters to local function declaration may produce invalid code.</source>
<target state="new">Warning: Adding parameters to local function declaration may produce invalid code.</target>
<note />
</trans-unit>
<trans-unit id="_0_is_not_null_here">
<source>'{0}' is not null here.</source>
<target state="new">'{0}' is not null here.</target>
......
......@@ -112,6 +112,11 @@
<target state="translated">Définir 'ref struct'</target>
<note>{Locked="ref"}{Locked="struct"} "ref" and "struct" are C# keywords and should not be localized.</note>
</trans-unit>
<trans-unit id="Pass_in_captured_variables_as_arguments">
<source>Pass in captured variables as arguments</source>
<target state="new">Pass in captured variables as arguments</target>
<note />
</trans-unit>
<trans-unit id="Remove_unnecessary_casts">
<source>Remove unnecessary casts</source>
<target state="translated">Supprimer les casts inutiles</target>
......@@ -162,6 +167,11 @@
<target state="new">Warning: Inlining temporary into conditional method call.</target>
<note />
</trans-unit>
<trans-unit id="Warning_colon_Adding_parameters_to_local_function_declaration_may_produce_invalid_code">
<source>Warning: Adding parameters to local function declaration may produce invalid code.</source>
<target state="new">Warning: Adding parameters to local function declaration may produce invalid code.</target>
<note />
</trans-unit>
<trans-unit id="_0_is_not_null_here">
<source>'{0}' is not null here.</source>
<target state="new">'{0}' is not null here.</target>
......
......@@ -112,6 +112,11 @@
<target state="translated">Imposta come 'ref struct'</target>
<note>{Locked="ref"}{Locked="struct"} "ref" and "struct" are C# keywords and should not be localized.</note>
</trans-unit>
<trans-unit id="Pass_in_captured_variables_as_arguments">
<source>Pass in captured variables as arguments</source>
<target state="new">Pass in captured variables as arguments</target>
<note />
</trans-unit>
<trans-unit id="Remove_unnecessary_casts">
<source>Remove unnecessary casts</source>
<target state="translated">Rimuovi i cast non necessari</target>
......@@ -162,6 +167,11 @@
<target state="new">Warning: Inlining temporary into conditional method call.</target>
<note />
</trans-unit>
<trans-unit id="Warning_colon_Adding_parameters_to_local_function_declaration_may_produce_invalid_code">
<source>Warning: Adding parameters to local function declaration may produce invalid code.</source>
<target state="new">Warning: Adding parameters to local function declaration may produce invalid code.</target>
<note />
</trans-unit>
<trans-unit id="_0_is_not_null_here">
<source>'{0}' is not null here.</source>
<target state="new">'{0}' is not null here.</target>
......
......@@ -112,6 +112,11 @@
<target state="translated">'ref struct' を作成します</target>
<note>{Locked="ref"}{Locked="struct"} "ref" and "struct" are C# keywords and should not be localized.</note>
</trans-unit>
<trans-unit id="Pass_in_captured_variables_as_arguments">
<source>Pass in captured variables as arguments</source>
<target state="new">Pass in captured variables as arguments</target>
<note />
</trans-unit>
<trans-unit id="Remove_unnecessary_casts">
<source>Remove unnecessary casts</source>
<target state="translated">不要なキャストを削除する</target>
......@@ -162,6 +167,11 @@
<target state="new">Warning: Inlining temporary into conditional method call.</target>
<note />
</trans-unit>
<trans-unit id="Warning_colon_Adding_parameters_to_local_function_declaration_may_produce_invalid_code">
<source>Warning: Adding parameters to local function declaration may produce invalid code.</source>
<target state="new">Warning: Adding parameters to local function declaration may produce invalid code.</target>
<note />
</trans-unit>
<trans-unit id="_0_is_not_null_here">
<source>'{0}' is not null here.</source>
<target state="new">'{0}' is not null here.</target>
......
......@@ -112,6 +112,11 @@
<target state="translated">'ref struct'로 지정</target>
<note>{Locked="ref"}{Locked="struct"} "ref" and "struct" are C# keywords and should not be localized.</note>
</trans-unit>
<trans-unit id="Pass_in_captured_variables_as_arguments">
<source>Pass in captured variables as arguments</source>
<target state="new">Pass in captured variables as arguments</target>
<note />
</trans-unit>
<trans-unit id="Remove_unnecessary_casts">
<source>Remove unnecessary casts</source>
<target state="translated">불필요한 캐스트 제거</target>
......@@ -162,6 +167,11 @@
<target state="new">Warning: Inlining temporary into conditional method call.</target>
<note />
</trans-unit>
<trans-unit id="Warning_colon_Adding_parameters_to_local_function_declaration_may_produce_invalid_code">
<source>Warning: Adding parameters to local function declaration may produce invalid code.</source>
<target state="new">Warning: Adding parameters to local function declaration may produce invalid code.</target>
<note />
</trans-unit>
<trans-unit id="_0_is_not_null_here">
<source>'{0}' is not null here.</source>
<target state="new">'{0}' is not null here.</target>
......
......@@ -112,6 +112,11 @@
<target state="translated">Ustaw jako „ref struct”</target>
<note>{Locked="ref"}{Locked="struct"} "ref" and "struct" are C# keywords and should not be localized.</note>
</trans-unit>
<trans-unit id="Pass_in_captured_variables_as_arguments">
<source>Pass in captured variables as arguments</source>
<target state="new">Pass in captured variables as arguments</target>
<note />
</trans-unit>
<trans-unit id="Remove_unnecessary_casts">
<source>Remove unnecessary casts</source>
<target state="translated">Usuń niepotrzebne rzutowania</target>
......@@ -162,6 +167,11 @@
<target state="new">Warning: Inlining temporary into conditional method call.</target>
<note />
</trans-unit>
<trans-unit id="Warning_colon_Adding_parameters_to_local_function_declaration_may_produce_invalid_code">
<source>Warning: Adding parameters to local function declaration may produce invalid code.</source>
<target state="new">Warning: Adding parameters to local function declaration may produce invalid code.</target>
<note />
</trans-unit>
<trans-unit id="_0_is_not_null_here">
<source>'{0}' is not null here.</source>
<target state="new">'{0}' is not null here.</target>
......
......@@ -112,6 +112,11 @@
<target state="translated">Alterar 'ref struct'</target>
<note>{Locked="ref"}{Locked="struct"} "ref" and "struct" are C# keywords and should not be localized.</note>
</trans-unit>
<trans-unit id="Pass_in_captured_variables_as_arguments">
<source>Pass in captured variables as arguments</source>
<target state="new">Pass in captured variables as arguments</target>
<note />
</trans-unit>
<trans-unit id="Remove_unnecessary_casts">
<source>Remove unnecessary casts</source>
<target state="translated">Remover conversões desnecessárias</target>
......@@ -162,6 +167,11 @@
<target state="new">Warning: Inlining temporary into conditional method call.</target>
<note />
</trans-unit>
<trans-unit id="Warning_colon_Adding_parameters_to_local_function_declaration_may_produce_invalid_code">
<source>Warning: Adding parameters to local function declaration may produce invalid code.</source>
<target state="new">Warning: Adding parameters to local function declaration may produce invalid code.</target>
<note />
</trans-unit>
<trans-unit id="_0_is_not_null_here">
<source>'{0}' is not null here.</source>
<target state="new">'{0}' is not null here.</target>
......
......@@ -112,6 +112,11 @@
<target state="translated">Сделать ref struct</target>
<note>{Locked="ref"}{Locked="struct"} "ref" and "struct" are C# keywords and should not be localized.</note>
</trans-unit>
<trans-unit id="Pass_in_captured_variables_as_arguments">
<source>Pass in captured variables as arguments</source>
<target state="new">Pass in captured variables as arguments</target>
<note />
</trans-unit>
<trans-unit id="Remove_unnecessary_casts">
<source>Remove unnecessary casts</source>
<target state="translated">Удалить ненужные приведения</target>
......@@ -162,6 +167,11 @@
<target state="new">Warning: Inlining temporary into conditional method call.</target>
<note />
</trans-unit>
<trans-unit id="Warning_colon_Adding_parameters_to_local_function_declaration_may_produce_invalid_code">
<source>Warning: Adding parameters to local function declaration may produce invalid code.</source>
<target state="new">Warning: Adding parameters to local function declaration may produce invalid code.</target>
<note />
</trans-unit>
<trans-unit id="_0_is_not_null_here">
<source>'{0}' is not null here.</source>
<target state="new">'{0}' is not null here.</target>
......
......@@ -112,6 +112,11 @@
<target state="translated">'ref struct' yap</target>
<note>{Locked="ref"}{Locked="struct"} "ref" and "struct" are C# keywords and should not be localized.</note>
</trans-unit>
<trans-unit id="Pass_in_captured_variables_as_arguments">
<source>Pass in captured variables as arguments</source>
<target state="new">Pass in captured variables as arguments</target>
<note />
</trans-unit>
<trans-unit id="Remove_unnecessary_casts">
<source>Remove unnecessary casts</source>
<target state="translated">Gereksiz atamaları kaldır</target>
......@@ -162,6 +167,11 @@
<target state="new">Warning: Inlining temporary into conditional method call.</target>
<note />
</trans-unit>
<trans-unit id="Warning_colon_Adding_parameters_to_local_function_declaration_may_produce_invalid_code">
<source>Warning: Adding parameters to local function declaration may produce invalid code.</source>
<target state="new">Warning: Adding parameters to local function declaration may produce invalid code.</target>
<note />
</trans-unit>
<trans-unit id="_0_is_not_null_here">
<source>'{0}' is not null here.</source>
<target state="new">'{0}' is not null here.</target>
......
......@@ -112,6 +112,11 @@
<target state="translated">生成 "ref struct"</target>
<note>{Locked="ref"}{Locked="struct"} "ref" and "struct" are C# keywords and should not be localized.</note>
</trans-unit>
<trans-unit id="Pass_in_captured_variables_as_arguments">
<source>Pass in captured variables as arguments</source>
<target state="new">Pass in captured variables as arguments</target>
<note />
</trans-unit>
<trans-unit id="Remove_unnecessary_casts">
<source>Remove unnecessary casts</source>
<target state="translated">删除不必要的转换</target>
......@@ -162,6 +167,11 @@
<target state="new">Warning: Inlining temporary into conditional method call.</target>
<note />
</trans-unit>
<trans-unit id="Warning_colon_Adding_parameters_to_local_function_declaration_may_produce_invalid_code">
<source>Warning: Adding parameters to local function declaration may produce invalid code.</source>
<target state="new">Warning: Adding parameters to local function declaration may produce invalid code.</target>
<note />
</trans-unit>
<trans-unit id="_0_is_not_null_here">
<source>'{0}' is not null here.</source>
<target state="new">'{0}' is not null here.</target>
......
......@@ -112,6 +112,11 @@
<target state="translated">設為 'ref struct'</target>
<note>{Locked="ref"}{Locked="struct"} "ref" and "struct" are C# keywords and should not be localized.</note>
</trans-unit>
<trans-unit id="Pass_in_captured_variables_as_arguments">
<source>Pass in captured variables as arguments</source>
<target state="new">Pass in captured variables as arguments</target>
<note />
</trans-unit>
<trans-unit id="Remove_unnecessary_casts">
<source>Remove unnecessary casts</source>
<target state="translated">移除不必要的 Cast</target>
......@@ -162,6 +167,11 @@
<target state="new">Warning: Inlining temporary into conditional method call.</target>
<note />
</trans-unit>
<trans-unit id="Warning_colon_Adding_parameters_to_local_function_declaration_may_produce_invalid_code">
<source>Warning: Adding parameters to local function declaration may produce invalid code.</source>
<target state="new">Warning: Adding parameters to local function declaration may produce invalid code.</target>
<note />
</trans-unit>
<trans-unit id="_0_is_not_null_here">
<source>'{0}' is not null here.</source>
<target state="new">'{0}' is not null here.</target>
......
......@@ -8,7 +8,6 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeGeneration;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Host;
......@@ -273,42 +272,33 @@ protected override TDeclarationNode AddNamespace<TDeclarationNode>(TDeclarationN
}
public override TDeclarationNode AddParameters<TDeclarationNode>(
TDeclarationNode destinationMember,
TDeclarationNode destination,
IEnumerable<IParameterSymbol> parameters,
CodeGenerationOptions options,
CancellationToken cancellationToken)
{
if (!(destinationMember is MemberDeclarationSyntax memberDeclaration))
{
return destinationMember;
}
var currentParameterList = CSharpSyntaxGenerator.GetParameterList(destination);
var currentParameterList = memberDeclaration.GetParameterList();
if (currentParameterList == null)
{
return destinationMember;
return destination;
}
var currentParamsCount = currentParameterList.Parameters.Count;
var seenOptional = currentParamsCount > 0 && currentParameterList.Parameters[currentParamsCount - 1].Default != null;
var isFirstParam = currentParamsCount == 0;
var newParams = ArrayBuilder<SyntaxNode>.GetInstance();
var parameterNodesAndTokens = currentParameterList.Parameters.GetWithSeparators().ToList();
foreach (var parameter in parameters)
{
if (parameterNodesAndTokens.Count > 0 && parameterNodesAndTokens.Last().Kind() != SyntaxKind.CommaToken)
{
parameterNodesAndTokens.Add(SyntaxFactory.Token(SyntaxKind.CommaToken));
}
var parameterSyntax = ParameterGenerator.GetParameter(parameter, options, isExplicit: false, isFirstParam: isFirstParam, seenOptional: seenOptional);
parameterNodesAndTokens.Add(parameterSyntax);
isFirstParam = false;
seenOptional = seenOptional || parameterSyntax.Default != null;
newParams.Add(parameterSyntax);
}
var finalParameterList = currentParameterList.WithParameters(SyntaxFactory.SeparatedList<ParameterSyntax>(parameterNodesAndTokens));
var finalMember = memberDeclaration.WithParameterList(finalParameterList);
var finalMember = CSharpSyntaxGenerator.Instance.AddParameters(destination, newParams.ToImmutableAndFree());
return Cast<TDeclarationNode>(finalMember);
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册