Skip to content

Commit 5af7e74

Browse files
authored
Merge pull request #736 from polyadic/generalize-match-analyzer
2 parents 8f54aea + 148a72f commit 5af7e74

23 files changed

+444
-180
lines changed

Funcky.Analyzers/Funcky.Analyzers.CodeFixes/OptionMatchToToNullableCodeFix.cs renamed to Funcky.Analyzers/Funcky.Analyzers.CodeFixes/AlternativeMonad/MatchToNullableCodeFix.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@
55
using Microsoft.CodeAnalysis.CodeFixes;
66
using Microsoft.CodeAnalysis.CSharp.Syntax;
77
using Microsoft.CodeAnalysis.Editing;
8+
using static Funcky.Analyzers.AlternativeMonadAnalyzer;
89
using static Funcky.Analyzers.FunckyWellKnownMemberNames;
9-
using static Funcky.Analyzers.OptionMatchAnalyzer;
1010
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
1111

12-
namespace Funcky.Analyzers;
12+
namespace Funcky.Analyzers.AlternativeMonad;
1313

1414
[Shared]
15-
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(OptionMatchToToNullableCodeFix))]
16-
public sealed class OptionMatchToToNullableCodeFix : CodeFixProvider
15+
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(MatchToNullableCodeFix))]
16+
public sealed class MatchToNullableCodeFix : CodeFixProvider
1717
{
1818
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(PreferToNullable.Id);
1919

@@ -48,7 +48,7 @@ public ToNullableCodeFixAction(
4848
_memberAccessExpression = memberAccessExpression;
4949
}
5050

51-
public override string Title => $"Replace {MatchMethodName} with {OptionToNullableMethodName}";
51+
public override string Title => $"Replace {MatchMethodName} with {ToNullableMethodName}";
5252

5353
public override string EquivalenceKey => nameof(ToNullableCodeFixAction);
5454

@@ -59,7 +59,7 @@ protected override async Task<Document> GetChangedDocumentAsync(CancellationToke
5959
editor.ReplaceNode(
6060
_invocationExpression,
6161
_invocationExpression.WithExpression(_memberAccessExpression
62-
.WithName(IdentifierName(OptionToNullableMethodName)))
62+
.WithName(IdentifierName(ToNullableMethodName)))
6363
.WithArgumentList(ArgumentList()));
6464

6565
return editor.GetChangedDocument();

Funcky.Analyzers/Funcky.Analyzers.CodeFixes/OptionMatchToOrElseCodeFix.cs renamed to Funcky.Analyzers/Funcky.Analyzers.CodeFixes/AlternativeMonad/MatchToOrElseCodeFix.cs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@
55
using Microsoft.CodeAnalysis.CodeFixes;
66
using Microsoft.CodeAnalysis.CSharp.Syntax;
77
using Microsoft.CodeAnalysis.Editing;
8+
using static Funcky.Analyzers.AlternativeMonadAnalyzer;
89
using static Funcky.Analyzers.FunckyWellKnownMemberNames;
9-
using static Funcky.Analyzers.OptionMatchAnalyzer;
1010
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
1111

12-
namespace Funcky.Analyzers;
12+
namespace Funcky.Analyzers.AlternativeMonad;
1313

1414
[Shared]
15-
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(OptionMatchToOrElseCodeFix))]
16-
public sealed class OptionMatchToOrElseCodeFix : CodeFixProvider
15+
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(MatchToOrElseCodeFix))]
16+
public sealed class MatchToOrElseCodeFix : CodeFixProvider
1717
{
1818
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(PreferGetOrElse.Id, PreferOrElse.Id, PreferSelectMany.Id);
1919

@@ -26,8 +26,8 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
2626
foreach (var diagnostic in context.Diagnostics)
2727
{
2828
if (syntaxRoot?.FindInvocationExpression(context.Span) is { Expression: MemberAccessExpressionSyntax memberAccessExpression } invocation
29-
&& diagnostic.Properties.TryGetValue(PreservedArgumentIndexProperty, out var noneArgumentIndexString)
30-
&& int.TryParse(noneArgumentIndexString, out var noneArgumentIndex))
29+
&& diagnostic.Properties.TryGetValue(PreservedArgumentIndexProperty, out var errorStateArgumentIndexString)
30+
&& int.TryParse(errorStateArgumentIndexString, out var noneArgumentIndex))
3131
{
3232
context.RegisterCodeFix(new GetOrElseCodeFixAction(context.Document, invocation, memberAccessExpression, noneArgumentIndex, DiagnosticIdToMethodName(diagnostic.Id)), diagnostic);
3333
}
@@ -48,26 +48,26 @@ private sealed class GetOrElseCodeFixAction : CodeAction
4848
private readonly Document _document;
4949
private readonly InvocationExpressionSyntax _invocationExpression;
5050
private readonly MemberAccessExpressionSyntax _memberAccessExpression;
51-
private readonly int _noneArgumentIndex;
51+
private readonly int _errorStateArgumentIndex;
5252
private readonly IdentifierNameSyntax _methodName;
5353

5454
public GetOrElseCodeFixAction(
5555
Document document,
5656
InvocationExpressionSyntax invocationExpression,
5757
MemberAccessExpressionSyntax memberAccessExpression,
58-
int noneArgumentIndex,
58+
int errorStateArgumentIndex,
5959
IdentifierNameSyntax methodName)
6060
{
6161
_document = document;
6262
_invocationExpression = invocationExpression;
6363
_memberAccessExpression = memberAccessExpression;
64-
_noneArgumentIndex = noneArgumentIndex;
64+
_errorStateArgumentIndex = errorStateArgumentIndex;
6565
_methodName = methodName;
6666
}
6767

6868
public override string Title => $"Replace {MatchMethodName} with {_methodName.Identifier}";
6969

70-
public override string? EquivalenceKey => nameof(OptionMatchToOrElseCodeFix);
70+
public override string? EquivalenceKey => nameof(MatchToOrElseCodeFix);
7171

7272
protected override async Task<Document> GetChangedDocumentAsync(CancellationToken cancellationToken)
7373
{
@@ -78,7 +78,7 @@ protected override async Task<Document> GetChangedDocumentAsync(CancellationToke
7878
_invocationExpression.WithExpression(_memberAccessExpression
7979
.WithName(_methodName))
8080
.WithArgumentList(ArgumentList(SingletonSeparatedList(
81-
Argument(_invocationExpression.ArgumentList.Arguments[_noneArgumentIndex].Expression)))));
81+
Argument(_invocationExpression.ArgumentList.Arguments[_errorStateArgumentIndex].Expression)))));
8282

8383
return editor.GetChangedDocument();
8484
}

Funcky.Analyzers/Funcky.Analyzers.CodeFixes/Funcky.Analyzers.CodeFixes.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,4 @@
2424
<Compile Update="CodeFixResources.Designer.cs" DesignTime="True" AutoGen="True" DependentUpon="CodeFixResources.resx" />
2525
<EmbeddedResource Update="CodeFixResources.resx" Generator="ResXFileCodeGenerator" LastGenOutput="CodeFixResources.Designer.cs" />
2626
</ItemGroup>
27-
2827
</Project>
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
namespace Funcky.Analyzers.Test;
2+
3+
public sealed partial class AlternativeMonadAnalyzerTest
4+
{
5+
// language=csharp
6+
private const string OptionStubCode =
7+
"""
8+
namespace Funcky.Monads
9+
{
10+
public readonly struct Option<TItem>
11+
where TItem : notnull
12+
{
13+
public static Option<TItem> None => default;
14+
15+
public static implicit operator Option<TItem>(TItem item) => default;
16+
17+
public TResult Match<TResult>(TResult none, System.Func<TItem, TResult> some) => default!;
18+
19+
public TResult Match<TResult>(System.Func<TResult> none, System.Func<TItem, TResult> some) => default!;
20+
21+
public TItem GetOrElse(TItem fallback) => default!;
22+
23+
public TItem GetOrElse(System.Func<TItem> fallback) => default!;
24+
25+
public Option<TItem> OrElse(Option<TItem> fallback) => default!;
26+
27+
public Option<TItem> OrElse(System.Func<Option<TItem>> fallback) => default!;
28+
29+
public Option<TResult> SelectMany<TResult>(System.Func<TItem, Option<TResult>> selector) where TResult : notnull => default;
30+
}
31+
32+
public static class OptionExtensions
33+
{
34+
public static TItem? ToNullable<TItem>(this Option<TItem> option, RequireStruct<TItem>? ω = null) where TItem : struct => default;
35+
36+
public static TItem? ToNullable<TItem>(this Option<TItem> option, RequireClass<TItem>? ω = null) where TItem : class => default;
37+
}
38+
39+
public static class Option
40+
{
41+
public static Option<TItem> Some<TItem>(TItem value) where TItem : notnull => default;
42+
43+
public static Option<TItem> Return<TItem>(TItem value) where TItem : notnull => default;
44+
}
45+
46+
public readonly struct Either<TLeft, TRight>
47+
where TLeft : notnull
48+
where TRight : notnull
49+
{
50+
public static Either<TLeft, TRight> Left(TLeft left) => default;
51+
52+
public static Either<TLeft, TRight> Right(TRight right) => default;
53+
54+
public TRight GetOrElse(TRight fallback) => default!;
55+
56+
public TRight GetOrElse(System.Func<TLeft, TRight> fallback) => default!;
57+
58+
public Either<TLeft, TRight> OrElse(Either<TLeft, TRight> fallback) => default;
59+
60+
public Either<TLeft, TRight> OrElse(System.Func<TLeft, Either<TLeft, TRight>> fallback) => default;
61+
62+
public Either<TLeft, TResult> SelectMany<TResult>(System.Func<TRight, Either<TLeft, TResult>> selector) where TResult : notnull => default;
63+
64+
public TMatchResult Match<TMatchResult>(System.Func<TLeft, TMatchResult> left, System.Func<TRight, TMatchResult> right) => default!;
65+
}
66+
67+
public static class Either<TLeft>
68+
where TLeft : notnull
69+
{
70+
public static Either<TLeft, TRight> Return<TRight>(TRight right)
71+
where TRight : notnull
72+
=> default;
73+
}
74+
75+
public readonly struct Result<TValidResult>
76+
where TValidResult : notnull
77+
{
78+
public static Result<TValidResult> Error(System.Exception exception) => default;
79+
80+
public Result<TValidResult> OrElse(Result<TValidResult> fallback) => default;
81+
82+
public Result<TValidResult> OrElse(System.Func<System.Exception, Result<TValidResult>> fallback) => default;
83+
84+
public TValidResult GetOrElse(TValidResult fallback) => default!;
85+
86+
public TValidResult GetOrElse(System.Func<System.Exception, TValidResult> fallback) => default!;
87+
88+
public Result<TResult> SelectMany<TResult>(System.Func<TValidResult, Result<TResult>> selector) where TResult : notnull => default;
89+
90+
public TMatchResult Match<TMatchResult>(System.Func<TValidResult, TMatchResult> ok, System.Func<System.Exception, TMatchResult> error) => default!;
91+
}
92+
93+
public static class Result
94+
{
95+
public static Result<TValidResult> Ok<TValidResult>(TValidResult result) where TValidResult : notnull => default;
96+
97+
public static Result<TValidResult> Return<TValidResult>(TValidResult result) where TValidResult : notnull => default;
98+
}
99+
100+
public sealed class RequireStruct<T> where T : struct { }
101+
102+
public sealed class RequireClass<T> where T : class { }
103+
}
104+
105+
namespace Funcky
106+
{
107+
public static class Functional
108+
{
109+
public static T Identity<T>(T x) => x;
110+
}
111+
}
112+
""";
113+
}

Funcky.Analyzers/Funcky.Analyzers.Test/OptionMatchAnalyzerTest.ToNullable.cs renamed to Funcky.Analyzers/Funcky.Analyzers.Test/AlternativeMonadAnalyzerTest.ToNullable.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
using Xunit;
2-
using static Funcky.Analyzers.OptionMatchAnalyzer;
3-
using VerifyCS = Funcky.Analyzers.Test.CSharpCodeFixVerifier<Funcky.Analyzers.OptionMatchAnalyzer, Funcky.Analyzers.OptionMatchToToNullableCodeFix>;
2+
using static Funcky.Analyzers.AlternativeMonadAnalyzer;
3+
using VerifyCS = Funcky.Analyzers.Test.CSharpCodeFixVerifier<Funcky.Analyzers.AlternativeMonadAnalyzer, Funcky.Analyzers.AlternativeMonad.MatchToNullableCodeFix>;
44

55
namespace Funcky.Analyzers.Test;
66

7-
public sealed partial class OptionMatchAnalyzerTest
7+
public sealed partial class AlternativeMonadAnalyzerTest
88
{
99
[Fact]
1010
public async Task WarnsAndFixesToNullableEquivalents()

0 commit comments

Comments
 (0)