diff --git a/src/EditorFeatures/CSharpTest/MakeLocalFunctionStatic/MakeLocalFunctionStaticRefactoringTests.cs b/src/EditorFeatures/CSharpTest/MakeLocalFunctionStatic/MakeLocalFunctionStaticRefactoringTests.cs new file mode 100644 index 0000000000000000000000000000000000000000..04681ce87e185a0c10987011400c8c42dd392f92 --- /dev/null +++ b/src/EditorFeatures/CSharpTest/MakeLocalFunctionStatic/MakeLocalFunctionStaticRefactoringTests.cs @@ -0,0 +1,481 @@ +// 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 del = AddLocal; + + int [||]AddLocal() + { + return x + 1; + } + } +}", +@"class C +{ + void N(int x) + { + Func 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); + } + } +} + diff --git a/src/EditorFeatures/CSharpTest/MakeLocalFunctionStatic/PassInCapturedVariablesAsArgumentsCodeFixProviderTests.cs b/src/EditorFeatures/CSharpTest/MakeLocalFunctionStatic/PassInCapturedVariablesAsArgumentsCodeFixProviderTests.cs new file mode 100644 index 0000000000000000000000000000000000000000..5fcfe6f75fccaf9dc59b4daed51f94a0dc0f5bc3 --- /dev/null +++ b/src/EditorFeatures/CSharpTest/MakeLocalFunctionStatic/PassInCapturedVariablesAsArgumentsCodeFixProviderTests.cs @@ -0,0 +1,467 @@ +// 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 del = AddLocal; + + static int AddLocal() + { + return [||]x + 1; + } + } +}", +@"class C +{ + void N(int x) + { + Func 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); + } + } +} + diff --git a/src/Features/CSharp/Portable/CSharpFeaturesResources.Designer.cs b/src/Features/CSharp/Portable/CSharpFeaturesResources.Designer.cs index 9a059a96190f7dee1f0abf0df0ffcf8772da46ac..593ea8c7a385d59366e6ace63bcb6f6d8a5ece2f 100644 --- a/src/Features/CSharp/Portable/CSharpFeaturesResources.Designer.cs +++ b/src/Features/CSharp/Portable/CSharpFeaturesResources.Designer.cs @@ -1015,6 +1015,15 @@ internal class CSharpFeaturesResources { } } + /// + /// Looks up a localized string similar to Pass in captured variables as arguments. + /// + internal static string Pass_in_captured_variables_as_arguments { + get { + return ResourceManager.GetString("Pass_in_captured_variables_as_arguments", resourceCulture); + } + } + /// /// Looks up a localized string similar to Properties. /// @@ -1548,6 +1557,16 @@ internal class CSharpFeaturesResources { } } + /// + /// Looks up a localized string similar to Warning: Adding parameters to local function declaration may produce invalid code.. + /// + 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); + } + } + /// /// Looks up a localized string similar to Warning: Moving using directives may change code meaning.. /// diff --git a/src/Features/CSharp/Portable/CSharpFeaturesResources.resx b/src/Features/CSharp/Portable/CSharpFeaturesResources.resx index 2b6933fd2d8eb8be8f202b9a96681e923472af5c..a5dba33fa0ba55e4ea7ca683036a6599f1e2dfba 100644 --- a/src/Features/CSharp/Portable/CSharpFeaturesResources.resx +++ b/src/Features/CSharp/Portable/CSharpFeaturesResources.resx @@ -669,4 +669,10 @@ switch statement case clause {Locked="switch"}{Locked="case"} "switch" and "case" are a C# keyword and should not be localized. + + Warning: Adding parameters to local function declaration may produce invalid code. + + + Pass in captured variables as arguments + \ No newline at end of file diff --git a/src/Features/CSharp/Portable/MakeLocalFunctionStatic/MakeLocalFunctionStaticCodeFixProvider.cs b/src/Features/CSharp/Portable/MakeLocalFunctionStatic/MakeLocalFunctionStaticCodeFixProvider.cs index b5e7c2f1549f1920afec55f5f2ce14df2577b7d0..5dfc544c263702bd920393381d8661f72b35af9d 100644 --- a/src/Features/CSharp/Portable/MakeLocalFunctionStatic/MakeLocalFunctionStaticCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/MakeLocalFunctionStatic/MakeLocalFunctionStaticCodeFixProvider.cs @@ -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; diff --git a/src/Features/CSharp/Portable/MakeLocalFunctionStatic/MakeLocalFunctionStaticCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/MakeLocalFunctionStatic/MakeLocalFunctionStaticCodeRefactoringProvider.cs new file mode 100644 index 0000000000000000000000000000000000000000..faa66d268a0237dea273ffd4d84a392f931e99b5 --- /dev/null +++ b/src/Features/CSharp/Portable/MakeLocalFunctionStatic/MakeLocalFunctionStaticCodeRefactoringProvider.cs @@ -0,0 +1,57 @@ +// 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().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> createChangedDocument) + : base(title, createChangedDocument) + { + } + } + } +} diff --git a/src/Features/CSharp/Portable/MakeLocalFunctionStatic/MakeLocalFunctionStaticDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/MakeLocalFunctionStatic/MakeLocalFunctionStaticDiagnosticAnalyzer.cs index c3ac3053e2ce5e971c237246e6a669e0517252fa..cfed8b8ad6f23dfd625fe0b75c493fcf6f266928 100644 --- a/src/Features/CSharp/Portable/MakeLocalFunctionStatic/MakeLocalFunctionStaticDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/MakeLocalFunctionStatic/MakeLocalFunctionStaticDiagnosticAnalyzer.cs @@ -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, diff --git a/src/Features/CSharp/Portable/MakeLocalFunctionStatic/MakeLocalFunctionStaticHelper.cs b/src/Features/CSharp/Portable/MakeLocalFunctionStatic/MakeLocalFunctionStaticHelper.cs new file mode 100644 index 0000000000000000000000000000000000000000..a3d83be3703e45dbfcd06ce46c57d4db79ab147b --- /dev/null +++ b/src/Features/CSharp/Portable/MakeLocalFunctionStatic/MakeLocalFunctionStaticHelper.cs @@ -0,0 +1,202 @@ +// 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 captures) + { + var dataFlow = semanticModel.AnalyzeDataFlow(localFunction); + captures = dataFlow.CapturedInside; + + return dataFlow.Succeeded; + } + + public static bool TryGetCaputuredSymbolsAndCheckApplicability(LocalFunctionStatementSyntax localFunction, SemanticModel semanticModel, out ImmutableArray captures) + => TryGetCaputuredSymbols(localFunction, semanticModel, out captures) && CanMakeLocalFunctionStatic(captures); + + private static bool CanMakeLocalFunctionStatic(ImmutableArray captures) + => captures.Length > 0 && !captures.Any(s => s.IsThisParameter()); + + public static async Task MakeLocalFunctionStaticAsync( + Document document, + LocalFunctionStatementSyntax localFunction, + ImmutableArray 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 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.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)); + + /// + /// Creates a new parameter symbol paired with the original captured symbol for each captured variables. + /// + private static ImmutableArray<(IParameterSymbol symbol, ISymbol capture)> CreateParameterSymbols(ImmutableArray 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(); + } + } +} diff --git a/src/Features/CSharp/Portable/MakeLocalFunctionStatic/PassInCapturedVariablesAsArgumentsCodeFixProvider.cs b/src/Features/CSharp/Portable/MakeLocalFunctionStatic/PassInCapturedVariablesAsArgumentsCodeFixProvider.cs new file mode 100644 index 0000000000000000000000000000000000000000..6ec2dfcffb8ebbf6731c2a2f8daf751d07a8dff1 --- /dev/null +++ b/src/Features/CSharp/Portable/MakeLocalFunctionStatic/PassInCapturedVariablesAsArgumentsCodeFixProvider.cs @@ -0,0 +1,111 @@ +// 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 ." + public override ImmutableArray 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 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 diagnostics, + Func, 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().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> createChangedDocument) + : base(CSharpFeaturesResources.Pass_in_captured_variables_as_arguments, createChangedDocument, CSharpFeaturesResources.Pass_in_captured_variables_as_arguments) + { + } + } + } +} diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf index 728adccf005255e8c05a1c89b538400caa5572f1..5de59cd3dd2723ad73b593a1c002e94536aacb5d 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf @@ -112,6 +112,11 @@ Nastavit jako ref struct {Locked="ref"}{Locked="struct"} "ref" and "struct" are C# keywords and should not be localized. + + Pass in captured variables as arguments + Pass in captured variables as arguments + + Remove unnecessary casts Odebrat nepotřebná přetypování @@ -162,6 +167,11 @@ Warning: Inlining temporary into conditional method call. + + Warning: Adding parameters to local function declaration may produce invalid code. + Warning: Adding parameters to local function declaration may produce invalid code. + + '{0}' is not null here. '{0}' is not null here. diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf index 28ed95dd3421ce6426c1f30cc701416a5fd5fc07..21f271938d2f1011cddc2753f8e1c3c738920db8 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf @@ -112,6 +112,11 @@ "ref struct" erstellen {Locked="ref"}{Locked="struct"} "ref" and "struct" are C# keywords and should not be localized. + + Pass in captured variables as arguments + Pass in captured variables as arguments + + Remove unnecessary casts Nicht erforderliche Umwandlungen entfernen @@ -162,6 +167,11 @@ Warning: Inlining temporary into conditional method call. + + Warning: Adding parameters to local function declaration may produce invalid code. + Warning: Adding parameters to local function declaration may produce invalid code. + + '{0}' is not null here. "{0}" ist hier nicht NULL. diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf index 7f1ef0bfa7e68280a91de3328e0a83f4afbfb49f..e6170d2fce731455b8add1343ec59f8c45dedaa5 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf @@ -112,6 +112,11 @@ Convertir "ref struct" {Locked="ref"}{Locked="struct"} "ref" and "struct" are C# keywords and should not be localized. + + Pass in captured variables as arguments + Pass in captured variables as arguments + + Remove unnecessary casts Quitar conversiones innecesarias @@ -162,6 +167,11 @@ Warning: Inlining temporary into conditional method call. + + Warning: Adding parameters to local function declaration may produce invalid code. + Warning: Adding parameters to local function declaration may produce invalid code. + + '{0}' is not null here. '{0}' is not null here. diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf index 9269e53bc4aa914f7dcd92d0cdc465a34156887f..6753ce67a39b8d2566e187c06865384d010a258f 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf @@ -112,6 +112,11 @@ Définir 'ref struct' {Locked="ref"}{Locked="struct"} "ref" and "struct" are C# keywords and should not be localized. + + Pass in captured variables as arguments + Pass in captured variables as arguments + + Remove unnecessary casts Supprimer les casts inutiles @@ -162,6 +167,11 @@ Warning: Inlining temporary into conditional method call. + + Warning: Adding parameters to local function declaration may produce invalid code. + Warning: Adding parameters to local function declaration may produce invalid code. + + '{0}' is not null here. '{0}' is not null here. diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf index 167b677ef2d8746a557375083a513cbf61fd9f65..50f4f5426689eba93ee9d66f68411a6dbdafa9bc 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf @@ -112,6 +112,11 @@ Imposta come 'ref struct' {Locked="ref"}{Locked="struct"} "ref" and "struct" are C# keywords and should not be localized. + + Pass in captured variables as arguments + Pass in captured variables as arguments + + Remove unnecessary casts Rimuovi i cast non necessari @@ -162,6 +167,11 @@ Warning: Inlining temporary into conditional method call. + + Warning: Adding parameters to local function declaration may produce invalid code. + Warning: Adding parameters to local function declaration may produce invalid code. + + '{0}' is not null here. '{0}' is not null here. diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf index c9e3fabff93f6f3a27be7667f9a93e15bb0a1385..427dab298d084a7ea853752515e020e333ec611b 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf @@ -112,6 +112,11 @@ 'ref struct' を作成します {Locked="ref"}{Locked="struct"} "ref" and "struct" are C# keywords and should not be localized. + + Pass in captured variables as arguments + Pass in captured variables as arguments + + Remove unnecessary casts 不要なキャストを削除する @@ -162,6 +167,11 @@ Warning: Inlining temporary into conditional method call. + + Warning: Adding parameters to local function declaration may produce invalid code. + Warning: Adding parameters to local function declaration may produce invalid code. + + '{0}' is not null here. '{0}' is not null here. diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf index 5278ddca9a6d975a3c0f269a9562de9669c441b2..ccf229389f1c35ebf9dd799db57ee0b093d89b49 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf @@ -112,6 +112,11 @@ 'ref struct'로 지정 {Locked="ref"}{Locked="struct"} "ref" and "struct" are C# keywords and should not be localized. + + Pass in captured variables as arguments + Pass in captured variables as arguments + + Remove unnecessary casts 불필요한 캐스트 제거 @@ -162,6 +167,11 @@ Warning: Inlining temporary into conditional method call. + + Warning: Adding parameters to local function declaration may produce invalid code. + Warning: Adding parameters to local function declaration may produce invalid code. + + '{0}' is not null here. '{0}' is not null here. diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf index f1fecbef2886d83152127f846c1d9c6ff6fe0692..1c3518f40f12ceab6e0877dc847f97c0512d5106 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf @@ -112,6 +112,11 @@ Ustaw jako „ref struct” {Locked="ref"}{Locked="struct"} "ref" and "struct" are C# keywords and should not be localized. + + Pass in captured variables as arguments + Pass in captured variables as arguments + + Remove unnecessary casts Usuń niepotrzebne rzutowania @@ -162,6 +167,11 @@ Warning: Inlining temporary into conditional method call. + + Warning: Adding parameters to local function declaration may produce invalid code. + Warning: Adding parameters to local function declaration may produce invalid code. + + '{0}' is not null here. '{0}' is not null here. diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf index e26f2b0fcc59c5bec9a616f25bb27455c921dabb..f2f2bd8639308e28fa92726661f531d53882d189 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf @@ -112,6 +112,11 @@ Alterar 'ref struct' {Locked="ref"}{Locked="struct"} "ref" and "struct" are C# keywords and should not be localized. + + Pass in captured variables as arguments + Pass in captured variables as arguments + + Remove unnecessary casts Remover conversões desnecessárias @@ -162,6 +167,11 @@ Warning: Inlining temporary into conditional method call. + + Warning: Adding parameters to local function declaration may produce invalid code. + Warning: Adding parameters to local function declaration may produce invalid code. + + '{0}' is not null here. '{0}' is not null here. diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf index 690c6a2bc327906c208313218f3611eba4ef6197..5e8657b8fae41cf2d5971f7d0e05d56ef17e4f02 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf @@ -112,6 +112,11 @@ Сделать ref struct {Locked="ref"}{Locked="struct"} "ref" and "struct" are C# keywords and should not be localized. + + Pass in captured variables as arguments + Pass in captured variables as arguments + + Remove unnecessary casts Удалить ненужные приведения @@ -162,6 +167,11 @@ Warning: Inlining temporary into conditional method call. + + Warning: Adding parameters to local function declaration may produce invalid code. + Warning: Adding parameters to local function declaration may produce invalid code. + + '{0}' is not null here. '{0}' is not null here. diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf index 0d4e6e6dea5d3923a548c87acd370eb14f130e4a..711836bac798bf8d1e8e95d9fba9972ec5ab61a5 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf @@ -112,6 +112,11 @@ 'ref struct' yap {Locked="ref"}{Locked="struct"} "ref" and "struct" are C# keywords and should not be localized. + + Pass in captured variables as arguments + Pass in captured variables as arguments + + Remove unnecessary casts Gereksiz atamaları kaldır @@ -162,6 +167,11 @@ Warning: Inlining temporary into conditional method call. + + Warning: Adding parameters to local function declaration may produce invalid code. + Warning: Adding parameters to local function declaration may produce invalid code. + + '{0}' is not null here. '{0}' is not null here. diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf index d4b6f5012d58b87adaaeece9e8f4697d8c0ec077..36d29753feead42a46446ccf1d9820af68ce4ba6 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf @@ -112,6 +112,11 @@ 生成 "ref struct" {Locked="ref"}{Locked="struct"} "ref" and "struct" are C# keywords and should not be localized. + + Pass in captured variables as arguments + Pass in captured variables as arguments + + Remove unnecessary casts 删除不必要的转换 @@ -162,6 +167,11 @@ Warning: Inlining temporary into conditional method call. + + Warning: Adding parameters to local function declaration may produce invalid code. + Warning: Adding parameters to local function declaration may produce invalid code. + + '{0}' is not null here. '{0}' is not null here. diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf index 548111e115f02d321ea624704a627e1221c06652..68730cb33467bc36408ff077fe19f2fe466b88ee 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf @@ -112,6 +112,11 @@ 設為 'ref struct' {Locked="ref"}{Locked="struct"} "ref" and "struct" are C# keywords and should not be localized. + + Pass in captured variables as arguments + Pass in captured variables as arguments + + Remove unnecessary casts 移除不必要的 Cast @@ -162,6 +167,11 @@ Warning: Inlining temporary into conditional method call. + + Warning: Adding parameters to local function declaration may produce invalid code. + Warning: Adding parameters to local function declaration may produce invalid code. + + '{0}' is not null here. '{0}' is not null here. diff --git a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpCodeGenerationService.cs b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpCodeGenerationService.cs index f093230402b66152eb4fa3bbef69fd6c2f7e9cc2..4ca1000bf0cebd523a3a742fe6ec943ca2f33b8d 100644 --- a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpCodeGenerationService.cs +++ b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpCodeGenerationService.cs @@ -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(TDeclarationN } public override TDeclarationNode AddParameters( - TDeclarationNode destinationMember, + TDeclarationNode destination, IEnumerable 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.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(parameterNodesAndTokens)); - var finalMember = memberDeclaration.WithParameterList(finalParameterList); + var finalMember = CSharpSyntaxGenerator.Instance.AddParameters(destination, newParams.ToImmutableAndFree()); return Cast(finalMember); }