diff --git a/src/EditorFeatures/CSharpTest/MakeMemberStatic/MakeMemberStaticTests.cs b/src/EditorFeatures/CSharpTest/MakeMemberStatic/MakeMemberStaticTests.cs index b8e700eafc5fb..256db27b5b997 100644 --- a/src/EditorFeatures/CSharpTest/MakeMemberStatic/MakeMemberStaticTests.cs +++ b/src/EditorFeatures/CSharpTest/MakeMemberStatic/MakeMemberStaticTests.cs @@ -2,53 +2,65 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - -using Microsoft.CodeAnalysis.CSharp.MakeMemberStatic; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics; +using Microsoft.CodeAnalysis.CSharp.MakeMemberStatic; +using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.CodeAnalysis.Testing; +using Roslyn.Test.Utilities; using Xunit; -using Xunit.Abstractions; namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.MakeMemberStatic { - public class MakeMemberStaticTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest + using VerifyCS = CSharpCodeFixVerifier< + EmptyDiagnosticAnalyzer, + CSharpMakeMemberStaticCodeFixProvider>; + + public class MakeMemberStaticTests { - public MakeMemberStaticTests(ITestOutputHelper logger) - : base(logger) + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMemberStatic)] + public async Task TestField() { + await VerifyCS.VerifyCodeFixAsync( +@" +public static class Foo +{ + int {|CS0708:i|}; +}", +@" +public static class Foo +{ + static int i; +}"); } - internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace) - => (null, new CSharpMakeMemberStaticCodeFixProvider()); - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMemberStatic)] - public async Task TestField() + [WorkItem(54202, "https://github.com/dotnet/roslyn/issues/54202")] + public async Task TestTrivia() { - await TestInRegularAndScript1Async( + await VerifyCS.VerifyCodeFixAsync( @" public static class Foo { - int [||]i; + // comment + readonly int {|CS0708:i|}; }", @" public static class Foo { - static int i; + // comment + static readonly int i; }"); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMemberStatic)] public async Task TestMethod() { - await TestInRegularAndScript1Async( + await VerifyCS.VerifyCodeFixAsync( @" public static class Foo { - void [||]M() { } + void {|CS0708:M|}() { } }", @" public static class Foo @@ -60,11 +72,11 @@ static void M() { } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMemberStatic)] public async Task TestProperty() { - await TestInRegularAndScript1Async( + await VerifyCS.VerifyCodeFixAsync( @" public static class Foo { - object [||]P { get; set; } + object {|CS0708:P|} { get; set; } }", @" public static class Foo @@ -76,11 +88,11 @@ public static class Foo [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMemberStatic)] public async Task TestEventField() { - await TestInRegularAndScript1Async( + await VerifyCS.VerifyCodeFixAsync( @" public static class Foo { - event System.Action [||]E; + event System.Action {|CS0708:E|}; }", @" public static class Foo @@ -92,15 +104,15 @@ public static class Foo [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMemberStatic)] public async Task FixAll() { - await TestInRegularAndScript1Async( + await VerifyCS.VerifyCodeFixAsync( @"namespace NS { public static class Foo { - int {|FixAllInDocument:|}i; - void M() { } - object P { get; set; } - event System.Action E; + int {|CS0708:i|}; + void {|CS0708:M|}() { } + object {|CS0708:P|} { get; set; } + event System.Action {|CS0708:E|}; } }", @"namespace NS diff --git a/src/Features/CSharp/Portable/MakeMemberStatic/CSharpMakeMemberStaticCodeFixProvider.cs b/src/Features/CSharp/Portable/MakeMemberStatic/CSharpMakeMemberStaticCodeFixProvider.cs index e2080313e09fd..eaecfd9a64afa 100644 --- a/src/Features/CSharp/Portable/MakeMemberStatic/CSharpMakeMemberStaticCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/MakeMemberStatic/CSharpMakeMemberStaticCodeFixProvider.cs @@ -2,14 +2,14 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - +using System; using System.Collections.Immutable; using System.Composition; using System.Diagnostics.CodeAnalysis; using System.Linq; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.MakeMemberStatic; namespace Microsoft.CodeAnalysis.CSharp.MakeMemberStatic @@ -18,7 +18,7 @@ namespace Microsoft.CodeAnalysis.CSharp.MakeMemberStatic internal sealed class CSharpMakeMemberStaticCodeFixProvider : AbstractMakeMemberStaticCodeFixProvider { [ImportingConstructor] - [SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public CSharpMakeMemberStaticCodeFixProvider() { } @@ -28,8 +28,22 @@ public CSharpMakeMemberStaticCodeFixProvider() "CS0708" // 'MyMethod': cannot declare instance members in a static class ); - protected override bool IsValidMemberNode(SyntaxNode node) => - node is MemberDeclarationSyntax || - (node.IsKind(SyntaxKind.VariableDeclarator) && node.Ancestors().Any(a => a.IsKind(SyntaxKind.FieldDeclaration) || a.IsKind(SyntaxKind.EventFieldDeclaration))); + protected override bool TryGetMemberDeclaration(SyntaxNode node, [NotNullWhen(true)] out SyntaxNode? memberDeclaration) + { + if (node is MemberDeclarationSyntax) + { + memberDeclaration = node; + return true; + } + + if (node.IsKind(SyntaxKind.VariableDeclarator) && node.Parent is VariableDeclarationSyntax { Parent: FieldDeclarationSyntax or EventFieldDeclarationSyntax }) + { + memberDeclaration = node.Parent.Parent; + return true; + } + + memberDeclaration = null; + return false; + } } } diff --git a/src/Features/Core/Portable/MakeMemberStatic/AbstractMakeMemberStaticCodeFixProvider.cs b/src/Features/Core/Portable/MakeMemberStatic/AbstractMakeMemberStaticCodeFixProvider.cs index e2dacdac3da69..9e1ebb32365e3 100644 --- a/src/Features/Core/Portable/MakeMemberStatic/AbstractMakeMemberStaticCodeFixProvider.cs +++ b/src/Features/Core/Portable/MakeMemberStatic/AbstractMakeMemberStaticCodeFixProvider.cs @@ -18,12 +18,12 @@ internal abstract class AbstractMakeMemberStaticCodeFixProvider : SyntaxEditorBa { internal sealed override CodeFixCategory CodeFixCategory => CodeFixCategory.Compile; - protected abstract bool IsValidMemberNode([NotNullWhen(true)] SyntaxNode? node); + protected abstract bool TryGetMemberDeclaration(SyntaxNode node, [NotNullWhen(true)] out SyntaxNode? memberDeclaration); public sealed override Task RegisterCodeFixesAsync(CodeFixContext context) { if (context.Diagnostics.Length == 1 && - IsValidMemberNode(context.Diagnostics[0].Location?.FindNode(context.CancellationToken))) + TryGetMemberDeclaration(context.Diagnostics[0].Location.FindNode(context.CancellationToken), out _)) { context.RegisterCodeFix( new MyCodeAction(c => FixAsync(context.Document, context.Diagnostics[0], c)), @@ -38,12 +38,13 @@ protected sealed override Task FixAllAsync(Document document, ImmutableArray generator.WithModifiers(currentDeclaration, DeclarationModifiers.Static)); + var generator = SyntaxGenerator.GetGenerator(document); + var newNode = generator.WithModifiers(memberDeclaration, generator.GetModifiers(declaration).WithIsStatic(true)); + editor.ReplaceNode(declaration, newNode); } }