Skip to content

Commit

Permalink
Extend make type abstract to include records (#48227)
Browse files Browse the repository at this point in the history
  • Loading branch information
Youssef1313 committed Nov 5, 2020
1 parent b5d7811 commit c73027c
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 72 deletions.
2 changes: 1 addition & 1 deletion src/Compilers/Test/Core/Traits/Traits.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,14 +116,14 @@ public static class Features
public const string CodeActionsInvertLogical = "CodeActions.InvertLogical";
public const string CodeActionsInvokeDelegateWithConditionalAccess = "CodeActions.InvokeDelegateWithConditionalAccess";
public const string CodeActionsLambdaSimplifier = "CodeActions.LambdaSimplifier";
public const string CodeActionsMakeClassAbstract = "CodeActions.MakeClassAbstract";
public const string CodeActionsMakeFieldReadonly = "CodeActions.MakeFieldReadonly";
public const string CodeActionsMakeLocalFunctionStatic = "CodeActions.MakeLocalFunctionStatic";
public const string CodeActionsMakeMethodAsynchronous = "CodeActions.MakeMethodAsynchronous";
public const string CodeActionsMakeMethodSynchronous = "CodeActions.MakeMethodSynchronous";
public const string CodeActionsMakeRefStruct = "CodeActions.MakeRefStruct";
public const string CodeActionsMakeStatementAsynchronous = "CodeActions.MakeStatementAsynchronous";
public const string CodeActionsMakeStructFieldsWritable = "CodeActions.MakeStructFieldsWritable";
public const string CodeActionsMakeTypeAbstract = "CodeActions.MakeTypeAbstract";
public const string CodeActionsMergeConsecutiveIfStatements = "CodeActions.MergeConsecutiveIfStatements";
public const string CodeActionsMergeNestedIfStatements = "CodeActions.MergeNestedIfStatements";
public const string CodeActionsMoveDeclarationNearReference = "CodeActions.MoveDeclarationNearReference";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,26 @@

using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.MakeClassAbstract;
using Microsoft.CodeAnalysis.CSharp.MakeTypeAbstract;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics;
using Microsoft.CodeAnalysis.Test.Utilities;
using Xunit;
using Xunit.Abstractions;

namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.MakeClassAbstract
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.MakeTypeAbstract
{
public class MakeClassAbstractTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest
public class MakeTypeAbstractTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest
{
public MakeClassAbstractTests(ITestOutputHelper logger)
public MakeTypeAbstractTests(ITestOutputHelper logger)
: base(logger)
{
}

internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace)
=> (null, new CSharpMakeClassAbstractCodeFixProvider());
=> (null, new CSharpMakeTypeAbstractCodeFixProvider());

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeClassAbstract)]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeTypeAbstract)]
public async Task TestMethod()
{
await TestInRegularAndScript1Async(
Expand All @@ -41,7 +41,7 @@ public abstract class Foo
}");
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeClassAbstract)]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeTypeAbstract)]
public async Task TestMethodEnclosingClassWithoutAccessibility()
{
await TestInRegularAndScript1Async(
Expand All @@ -57,7 +57,7 @@ abstract class Foo
}");
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeClassAbstract)]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeTypeAbstract)]
public async Task TestMethodEnclosingClassDocumentationComment()
{
await TestInRegularAndScript1Async(
Expand All @@ -79,7 +79,7 @@ public abstract class Foo
}");
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeClassAbstract)]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeTypeAbstract)]
public async Task TestPropertyGetter()
{
await TestInRegularAndScript1Async(
Expand All @@ -95,7 +95,7 @@ public abstract class Foo
}");
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeClassAbstract)]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeTypeAbstract)]
public async Task TestPropertySetter()
{
await TestInRegularAndScript1Async(
Expand All @@ -111,7 +111,7 @@ public abstract class Foo
}");
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeClassAbstract)]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeTypeAbstract)]
public async Task TestIndexerGetter()
{
await TestInRegularAndScript1Async(
Expand All @@ -127,7 +127,7 @@ public abstract class Foo
}");
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeClassAbstract)]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeTypeAbstract)]
public async Task TestIndexerSetter()
{
await TestInRegularAndScript1Async(
Expand All @@ -143,7 +143,7 @@ public abstract class Foo
}");
}

[Fact(Skip = "https://github.com/dotnet/roslyn/issues/41654"), Trait(Traits.Feature, Traits.Features.CodeActionsMakeClassAbstract)]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/41654"), Trait(Traits.Feature, Traits.Features.CodeActionsMakeTypeAbstract)]
public async Task TestPartialClass()
{
await TestInRegularAndScript1Async(
Expand All @@ -167,7 +167,7 @@ public partial class Foo
}");
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeClassAbstract)]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeTypeAbstract)]
public async Task TestEventAdd()
{
await TestMissingInRegularAndScriptAsync(
Expand All @@ -178,7 +178,7 @@ public class Foo
}");
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeClassAbstract)]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeTypeAbstract)]
public async Task TestEventRemove()
{
await TestMissingInRegularAndScriptAsync(
Expand All @@ -189,7 +189,7 @@ public class Foo
}");
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeClassAbstract)]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeTypeAbstract)]
public async Task TestMethodWithBody()
{
await TestMissingInRegularAndScriptAsync(
Expand All @@ -200,7 +200,7 @@ public class Foo
}");
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeClassAbstract)]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeTypeAbstract)]
public async Task TestPropertyGetterWithArrowBody()
{
await TestMissingInRegularAndScriptAsync(
Expand All @@ -211,7 +211,7 @@ public class Foo
}");
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeClassAbstract)]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeTypeAbstract)]
public async Task TestPropertyGetterWithBody()
{
await TestMissingInRegularAndScriptAsync(
Expand All @@ -225,7 +225,7 @@ public class Foo
}");
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeClassAbstract)]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeTypeAbstract)]
public async Task TestStructNestedInClass()
{
await TestMissingInRegularAndScriptAsync(
Expand All @@ -239,7 +239,7 @@ public struct S
}");
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeClassAbstract)]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeTypeAbstract)]
public async Task TestMethodEnclosingClassStatic()
{
await TestMissingInRegularAndScriptAsync(
Expand All @@ -250,7 +250,23 @@ public static class Foo
}");
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeClassAbstract)]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeTypeAbstract)]
public async Task TestRecord()
{
await TestInRegularAndScript1Async(
@"
public record Foo
{
public abstract void [|M|]();
}",
@"
public abstract record Foo
{
public abstract void M();
}");
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeTypeAbstract)]
public async Task FixAll()
{
await TestInRegularAndScript1Async(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@
Imports Microsoft.CodeAnalysis.CodeFixes
Imports Microsoft.CodeAnalysis.Diagnostics
Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics
Imports Microsoft.CodeAnalysis.VisualBasic.MakeClassAbstract
Imports Microsoft.CodeAnalysis.VisualBasic.MakeTypeAbstract

Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.MakeClassAbstract
Public Class MakeClassAbstractTests
Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.MakeTypeAbstract
Public Class MakeTypeAbstractTests
Inherits AbstractVisualBasicDiagnosticProviderBasedUserDiagnosticTest

Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace) As (DiagnosticAnalyzer, CodeFixProvider)
Return (Nothing, New VisualBasicMakeClassAbstractCodeFixProvider())
Return (Nothing, New VisualBasicMakeTypeAbstractCodeFixProvider())
End Function

<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeClassAbstract)>
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeTypeAbstract)>
Public Async Function TestMethod_CodeFix() As Task
Await TestInRegularAndScript1Async("
Public Class [|Foo|]
Expand All @@ -27,15 +27,15 @@ Public MustOverride Class Foo
End Class")
End Function

<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeClassAbstract)>
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeTypeAbstract)>
Public Async Function TestMethodEnclosingClassWithoutAccessibility_NoCodeFix() As Task
Await TestMissingInRegularAndScriptAsync("
Class Foo
Public MustOverride Sub [|M|]()
End Class")
End Function

<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeClassAbstract)>
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeTypeAbstract)>
Public Async Function TestMethodEnclosingClassDocumentationComment() As Task
Await TestMissingInRegularAndScriptAsync("
''' <summary>
Expand All @@ -46,31 +46,31 @@ Public Class Foo
End Class")
End Function

<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeClassAbstract)>
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeTypeAbstract)>
Public Async Function TestProperty() As Task
Await TestMissingInRegularAndScriptAsync("
Public Class Foo
Public MustOverride Property [|P|] As Object
End Class")
End Function

<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeClassAbstract)>
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeTypeAbstract)>
Public Async Function TestIndexer() As Task
Await TestMissingInRegularAndScriptAsync("
Public Class Foo
Default Public MustOverride Property [|Item|](ByVal o As Object) As Object
End Class")
End Function

<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeClassAbstract)>
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeTypeAbstract)>
Public Async Function TestEvent() As Task
Await TestMissingInRegularAndScriptAsync("
Public Class Foo
Public MustOverride Custom Event [|E|] As EventHandler
End Class")
End Function

<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeClassAbstract)>
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeTypeAbstract)>
Public Async Function TestMethodWithBody() As Task
Await TestMissingInRegularAndScriptAsync("
Public Class Foo
Expand All @@ -80,7 +80,7 @@ Public Class Foo
End Class")
End Function

<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeClassAbstract)>
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeTypeAbstract)>
Public Async Function TestPropertyWithBody() As Task
Await TestMissingInRegularAndScriptAsync(
"
Expand All @@ -93,7 +93,7 @@ Public Class Foo
End Class")
End Function

<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeClassAbstract)>
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeTypeAbstract)>
Public Async Function TestStructNestedInClass() As Task
Await TestMissingInRegularAndScriptAsync(
"
Expand All @@ -104,7 +104,7 @@ Public Class C
End Class")
End Function

<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeClassAbstract)>
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeTypeAbstract)>
Public Async Function TestMethodEnclosingClassStatic() As Task
Await TestMissingInRegularAndScriptAsync(
"
Expand All @@ -113,7 +113,7 @@ Public Static Class Foo
End Class")
End Function

<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeClassAbstract)>
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeTypeAbstract)>
Public Async Function FixAll() As Task
Await TestMissingInRegularAndScriptAsync("
Namespace NS
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,60 +8,59 @@
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.MakeClassAbstract;
using Microsoft.CodeAnalysis.MakeTypeAbstract;

namespace Microsoft.CodeAnalysis.CSharp.MakeClassAbstract
namespace Microsoft.CodeAnalysis.CSharp.MakeTypeAbstract
{
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(CSharpMakeClassAbstractCodeFixProvider)), Shared]
internal sealed class CSharpMakeClassAbstractCodeFixProvider : AbstractMakeClassAbstractCodeFixProvider<ClassDeclarationSyntax>
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(CSharpMakeTypeAbstractCodeFixProvider)), Shared]
internal sealed class CSharpMakeTypeAbstractCodeFixProvider : AbstractMakeTypeAbstractCodeFixProvider<TypeDeclarationSyntax>
{
[ImportingConstructor]
[SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")]
public CSharpMakeClassAbstractCodeFixProvider()
public CSharpMakeTypeAbstractCodeFixProvider()
{
}

public override ImmutableArray<string> FixableDiagnosticIds { get; } =
ImmutableArray.Create(
"CS0513" // 'C.M()' is abstract but it is contained in non-abstract class 'C'
"CS0513" // 'C.M()' is abstract but it is contained in non-abstract type 'C'
);

protected override bool IsValidRefactoringContext(SyntaxNode? node, [NotNullWhen(true)] out ClassDeclarationSyntax? classDeclaration)
protected override bool IsValidRefactoringContext(SyntaxNode? node, [NotNullWhen(true)] out TypeDeclarationSyntax? typeDeclaration)
{
classDeclaration = null;

switch (node?.Kind())
switch (node)
{
case SyntaxKind.MethodDeclaration:
var method = (MethodDeclarationSyntax)node;
case MethodDeclarationSyntax method:
if (method.Body != null || method.ExpressionBody != null)
{
typeDeclaration = null;
return false;
}
break;

case SyntaxKind.GetAccessorDeclaration:
case SyntaxKind.SetAccessorDeclaration:
var accessor = (AccessorDeclarationSyntax)node;
case AccessorDeclarationSyntax accessor:
if (accessor.Body != null || accessor.ExpressionBody != null)
{
typeDeclaration = null;
return false;
}
break;

default:
typeDeclaration = null;
return false;
}

var enclosingType = node.FirstAncestorOrSelf<TypeDeclarationSyntax>();
if (!enclosingType.IsKind(SyntaxKind.ClassDeclaration))
if ((enclosingType.IsKind(SyntaxKind.ClassDeclaration) || enclosingType.IsKind(SyntaxKind.RecordDeclaration)) &&
!enclosingType.Modifiers.Any(SyntaxKind.AbstractKeyword) && !enclosingType.Modifiers.Any(SyntaxKind.StaticKeyword))
{
return false;
typeDeclaration = enclosingType;
return true;
}

classDeclaration = (ClassDeclarationSyntax)enclosingType;

return !classDeclaration.Modifiers.Any(SyntaxKind.AbstractKeyword) && !classDeclaration.Modifiers.Any(SyntaxKind.StaticKeyword);
typeDeclaration = null;
return false;
}
}
}
Loading

0 comments on commit c73027c

Please sign in to comment.