Skip to content

Commit 57df4d9

Browse files
authored
Merge pull request #183 from koenbeuk/feature/factory-method-fixer
Add a code fixer and a code refactorer to transform factory methods into constructors
2 parents 76eff10 + 4711fbc commit 57df4d9

41 files changed

Lines changed: 2430 additions & 5 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

samples/ReadmeSample/ReadmeSample.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
<ItemGroup>
1515
<ProjectReference Include="..\..\src\EntityFrameworkCore.Projectables.Generator\EntityFrameworkCore.Projectables.Generator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
16+
<ProjectReference Include="..\..\src\EntityFrameworkCore.Projectables.CodeFixes\EntityFrameworkCore.Projectables.CodeFixes.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
1617
<ProjectReference Include="..\..\src\EntityFrameworkCore.Projectables\EntityFrameworkCore.Projectables.csproj" />
1718
</ItemGroup>
1819

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
using System.Composition;
2+
using Microsoft.CodeAnalysis;
3+
using Microsoft.CodeAnalysis.CodeActions;
4+
using Microsoft.CodeAnalysis.CodeRefactorings;
5+
6+
namespace EntityFrameworkCore.Projectables.CodeFixes;
7+
8+
/// <summary>
9+
/// Code refactoring provider that converts a <c>[Projectable]</c> factory method whose body is
10+
/// an object-initializer expression (<c>=> new T { … }</c>) into a <c>[Projectable]</c>
11+
/// constructor of the same class.
12+
/// <para>
13+
/// Two refactoring actions are offered:
14+
/// <list type="number">
15+
/// <item><description>Convert the factory method to a constructor (current document only).</description></item>
16+
/// <item><description>Convert the factory method to a constructor <em>and</em> replace all
17+
/// callers throughout the solution with <c>new T(…)</c> invocations.</description></item>
18+
/// </list>
19+
/// </para>
20+
/// <para>
21+
/// This provider is complementary to <see cref="FactoryMethodToCtorCodeFixProvider"/>,
22+
/// which fixes the <c>EFP0012</c> diagnostic. The refactoring provider remains useful when
23+
/// the diagnostic is suppressed.
24+
/// </para>
25+
/// <para>
26+
/// A public parameterless constructor is automatically inserted when the class does not already
27+
/// have one, preserving the implicit default constructor that would otherwise be lost.
28+
/// </para>
29+
/// </summary>
30+
[ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = nameof(FactoryMethodToConstructorCodeRefactoringProvider))]
31+
[Shared]
32+
public sealed class FactoryMethodToConstructorCodeRefactoringProvider : CodeRefactoringProvider
33+
{
34+
/// <inheritdoc />
35+
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
36+
{
37+
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
38+
if (root is null)
39+
{
40+
return;
41+
}
42+
43+
var node = root.FindNode(context.Span);
44+
45+
if (!ProjectableCodeFixHelper.TryGetFixableFactoryMethodPattern(node, out var containingType, out var method))
46+
{
47+
return;
48+
}
49+
50+
context.RegisterRefactoring(
51+
CodeAction.Create(
52+
title: "Convert [Projectable] factory method to constructor",
53+
createChangedDocument: ct =>
54+
FactoryMethodTransformationHelper.ConvertToConstructorAsync(
55+
context.Document, method!, containingType!, ct),
56+
equivalenceKey: "EFP_FactoryToConstructor"));
57+
58+
context.RegisterRefactoring(
59+
CodeAction.Create(
60+
title: "Convert [Projectable] factory method to constructor (and update callers)",
61+
createChangedSolution: ct =>
62+
FactoryMethodTransformationHelper.ConvertToConstructorAndUpdateCallersAsync(
63+
context.Document, method!, containingType!, ct),
64+
equivalenceKey: "EFP_FactoryToConstructorWithCallers"));
65+
}
66+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
using System.Collections.Immutable;
2+
using System.Composition;
3+
using Microsoft.CodeAnalysis;
4+
using Microsoft.CodeAnalysis.CodeActions;
5+
using Microsoft.CodeAnalysis.CodeFixes;
6+
7+
namespace EntityFrameworkCore.Projectables.CodeFixes;
8+
9+
/// <summary>
10+
/// Code fix provider for <c>EFP0012</c>.
11+
/// Offers two fixes on a <c>[Projectable]</c> factory method that can be a constructor:
12+
/// <list type="number">
13+
/// <item><description>Convert the factory method to a constructor (current document).</description></item>
14+
/// <item><description>Convert the factory method to a constructor <em>and</em> update all
15+
/// callers throughout the solution.</description></item>
16+
/// </list>
17+
/// </summary>
18+
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(FactoryMethodToCtorCodeFixProvider))]
19+
[Shared]
20+
public sealed class FactoryMethodToCtorCodeFixProvider : CodeFixProvider
21+
{
22+
/// <inheritdoc />
23+
public override ImmutableArray<string> FixableDiagnosticIds => ["EFP0012"];
24+
25+
/// <inheritdoc />
26+
public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;
27+
28+
/// <inheritdoc />
29+
public override async Task RegisterCodeFixesAsync(CodeFixContext context)
30+
{
31+
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
32+
if (root is null)
33+
{
34+
return;
35+
}
36+
37+
var node = root.FindNode(context.Span);
38+
39+
if (!ProjectableCodeFixHelper.TryGetFixableFactoryMethodPattern(node, out var containingType, out var method))
40+
{
41+
return;
42+
}
43+
44+
context.RegisterCodeFix(
45+
CodeAction.Create(
46+
title: "Convert [Projectable] factory method to constructor",
47+
createChangedDocument: ct =>
48+
FactoryMethodTransformationHelper.ConvertToConstructorAsync(
49+
context.Document, method!, containingType!, ct),
50+
equivalenceKey: "EFP0012_FactoryToConstructor"),
51+
context.Diagnostics[0]);
52+
53+
context.RegisterCodeFix(
54+
CodeAction.Create(
55+
title: "Convert [Projectable] factory method to constructor (and update callers)",
56+
createChangedSolution: ct =>
57+
FactoryMethodTransformationHelper.ConvertToConstructorAndUpdateCallersAsync(
58+
context.Document, method!, containingType!, ct),
59+
equivalenceKey: "EFP0012_FactoryToConstructorWithCallers"),
60+
context.Diagnostics[0]);
61+
}
62+
}
63+

0 commit comments

Comments
 (0)