Skip to content

Commit 2be0b92

Browse files
committed
Extract common code used repeatedly in code fixes
1 parent a70c9fc commit 2be0b92

5 files changed

Lines changed: 198 additions & 271 deletions

File tree

src/NetEscapades.EnumGenerators/Diagnostics/CodeFixProviderBase.cs

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System.Collections.Immutable;
22
using Microsoft.CodeAnalysis;
33
using Microsoft.CodeAnalysis.CodeFixes;
4+
using Microsoft.CodeAnalysis.Editing;
45

56
namespace NetEscapades.EnumGenerators.Diagnostics;
67

@@ -42,6 +43,48 @@ public sealed override FixAllProvider GetFixAllProvider()
4243
);
4344
}
4445

45-
protected abstract Task<Document> FixAllAsync(
46-
Document document, ImmutableArray<Diagnostic> diagnostics, CancellationToken cancellationToken);
46+
protected Task<Document> FixAllAsync(
47+
Document document,
48+
ImmutableArray<Diagnostic> diagnostics,
49+
CancellationToken cancellationToken)
50+
=> FixAllAsync(document, diagnostics, FixWithEditor, cancellationToken);
51+
52+
private static async Task<Document> FixAllAsync(
53+
Document document,
54+
ImmutableArray<Diagnostic> diagnostics,
55+
Func<DocumentEditor, Diagnostic, INamedTypeSymbol, CancellationToken, Task> fixFunc,
56+
CancellationToken cancellationToken)
57+
{
58+
// Create the new identifier with "HasFlagFast"
59+
var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false);
60+
if (editor is null)
61+
{
62+
return document;
63+
}
64+
65+
foreach (var diagnostic in diagnostics)
66+
{
67+
cancellationToken.ThrowIfCancellationRequested();
68+
if (!diagnostic.Properties.TryGetValue(AnalyzerHelpers.ExtensionTypeNameProperty, out var extensionTypeName)
69+
|| extensionTypeName is null)
70+
{
71+
continue;
72+
}
73+
74+
var type = editor.SemanticModel.Compilation.GetTypeByMetadataName(extensionTypeName);
75+
if (type is null)
76+
{
77+
continue;
78+
}
79+
80+
await fixFunc(editor, diagnostic, type, cancellationToken);
81+
}
82+
83+
return editor.GetChangedDocument();
84+
}
85+
86+
87+
protected abstract Task FixWithEditor(
88+
DocumentEditor editor, Diagnostic diagnostic, INamedTypeSymbol extensionTypeSymbol, CancellationToken cancellationToken);
89+
4790
}

src/NetEscapades.EnumGenerators/Diagnostics/UsageAnalyzers/HasFlagCodeFixProvider.cs

Lines changed: 21 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -25,70 +25,40 @@ public sealed override Task RegisterCodeFixesAsync(CodeFixContext context)
2525
context.RegisterCodeFix(
2626
CodeAction.Create(
2727
title: Title,
28-
createChangedDocument: c => ReplaceHasFlagWithHasFlagFast(context.Document, context.Diagnostics, c),
28+
createChangedDocument: c => FixAllAsync(context.Document, context.Diagnostics, c),
2929
equivalenceKey: Title),
3030
context.Diagnostics);
3131
}
3232

3333
return Task.CompletedTask;
3434
}
3535

36-
protected sealed override Task<Document> FixAllAsync(Document document, ImmutableArray<Diagnostic> diagnostics, CancellationToken cancellationToken)
37-
=> ReplaceHasFlagWithHasFlagFast(document, diagnostics, cancellationToken);
38-
39-
private static async Task<Document> ReplaceHasFlagWithHasFlagFast(
40-
Document document,
41-
ImmutableArray<Diagnostic> diagnostics,
36+
protected override Task FixWithEditor(DocumentEditor editor, Diagnostic diagnostic, INamedTypeSymbol extensionTypeSymbol,
4237
CancellationToken cancellationToken)
4338
{
44-
// Create the new identifier with "HasFlagFast"
45-
var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false);
46-
if (editor is null)
47-
{
48-
return document;
49-
}
50-
5139
var generator = editor.Generator;
52-
var semanticModel = editor.SemanticModel;
53-
54-
foreach (var diagnostic in diagnostics)
55-
{
56-
cancellationToken.ThrowIfCancellationRequested();
57-
if (!diagnostic.Properties.TryGetValue(AnalyzerHelpers.ExtensionTypeNameProperty, out var extensionTypeName)
58-
|| extensionTypeName is null)
59-
{
60-
continue;
61-
}
6240

63-
// Find the node at the diagnostic location
64-
var node = editor.OriginalRoot.FindNode(diagnostic.Location.SourceSpan);
41+
// Find the node at the diagnostic location
42+
var node = editor.OriginalRoot.FindNode(diagnostic.Location.SourceSpan);
6543

66-
if (node is not IdentifierNameSyntax identifierName
67-
|| identifierName.Parent is not MemberAccessExpressionSyntax memberAccess
68-
|| memberAccess.Parent is not InvocationExpressionSyntax invocation)
69-
{
70-
continue;
71-
}
72-
73-
var type = semanticModel.Compilation.GetTypeByMetadataName(extensionTypeName);
74-
if (type is null)
75-
{
76-
continue;
77-
}
78-
79-
var newInvocation = generator.InvocationExpression(
80-
generator.MemberAccessExpression(generator.TypeExpression(type), "HasFlagFast"),
81-
[
82-
memberAccess.Expression, // this parameter
83-
..invocation.ArgumentList.Arguments,
84-
])
85-
.WithTriviaFrom(invocation)
86-
.WithAdditionalAnnotations(Simplifier.AddImportsAnnotation, Simplifier.Annotation);
87-
88-
// Create new member access with the new identifier
89-
editor.ReplaceNode(invocation, newInvocation);
44+
if (node is not IdentifierNameSyntax identifierName
45+
|| identifierName.Parent is not MemberAccessExpressionSyntax memberAccess
46+
|| memberAccess.Parent is not InvocationExpressionSyntax invocation)
47+
{
48+
return Task.CompletedTask;
9049
}
9150

92-
return editor.GetChangedDocument();
51+
var newInvocation = generator.InvocationExpression(
52+
generator.MemberAccessExpression(generator.TypeExpression(extensionTypeSymbol), "HasFlagFast"),
53+
[
54+
memberAccess.Expression, // this parameter
55+
..invocation.ArgumentList.Arguments,
56+
])
57+
.WithTriviaFrom(invocation)
58+
.WithAdditionalAnnotations(Simplifier.AddImportsAnnotation, Simplifier.Annotation);
59+
60+
// Create new member access with the new identifier
61+
editor.ReplaceNode(invocation, newInvocation);
62+
return Task.CompletedTask;
9363
}
9464
}

src/NetEscapades.EnumGenerators/Diagnostics/UsageAnalyzers/IsDefinedCodeFixProvider.cs

Lines changed: 40 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -25,93 +25,64 @@ public sealed override Task RegisterCodeFixesAsync(CodeFixContext context)
2525
context.RegisterCodeFix(
2626
CodeAction.Create(
2727
title: Title,
28-
createChangedDocument: c => ReplaceIsDefinedWithGenerated(context.Document, context.Diagnostics, c),
28+
createChangedDocument: c => FixAllAsync(context.Document, context.Diagnostics, c),
2929
equivalenceKey: Title),
3030
context.Diagnostics);
3131
}
3232

3333
return Task.CompletedTask;
3434
}
3535

36-
protected sealed override Task<Document> FixAllAsync(Document document, ImmutableArray<Diagnostic> diagnostics, CancellationToken cancellationToken)
37-
=> ReplaceIsDefinedWithGenerated(document, diagnostics, cancellationToken);
38-
39-
private static async Task<Document> ReplaceIsDefinedWithGenerated(
40-
Document document,
41-
ImmutableArray<Diagnostic> diagnostics,
36+
protected override Task FixWithEditor(DocumentEditor editor, Diagnostic diagnostic,
37+
INamedTypeSymbol extensionTypeSymbol,
4238
CancellationToken cancellationToken)
4339
{
44-
var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false);
45-
if (editor is null)
40+
// Find the invocation node at the diagnostic location
41+
var node = editor.OriginalRoot.FindNode(diagnostic.Location.SourceSpan);
42+
if (node is not InvocationExpressionSyntax invocation)
4643
{
47-
return document;
44+
return Task.CompletedTask;
4845
}
4946

50-
var generator = editor.Generator;
51-
var semanticModel = editor.SemanticModel;
52-
53-
foreach (var diagnostic in diagnostics)
47+
// Get the symbol to determine which pattern we're dealing with
48+
var symbolInfo = editor.SemanticModel.GetSymbolInfo(invocation, cancellationToken);
49+
if (symbolInfo.Symbol is not IMethodSymbol methodSymbol)
5450
{
55-
cancellationToken.ThrowIfCancellationRequested();
56-
if (!diagnostic.Properties.TryGetValue(AnalyzerHelpers.ExtensionTypeNameProperty, out var extensionTypeName)
57-
|| extensionTypeName is null)
58-
{
59-
continue;
60-
}
61-
62-
// Find the invocation node at the diagnostic location
63-
var node = editor.OriginalRoot.FindNode(diagnostic.Location.SourceSpan);
64-
if (node is not InvocationExpressionSyntax invocation)
65-
{
66-
continue;
67-
}
68-
69-
// Get the symbol to determine which pattern we're dealing with
70-
var symbolInfo = semanticModel.GetSymbolInfo(invocation);
71-
if (symbolInfo.Symbol is not IMethodSymbol methodSymbol)
72-
{
73-
continue;
74-
}
75-
76-
ArgumentSyntax? valueArgument = null;
51+
return Task.CompletedTask;
52+
}
7753

78-
// Determine which argument is the value to check
79-
if (methodSymbol is { IsGenericMethod: true, TypeArguments.Length: 1 })
80-
{
81-
// Pattern: Enum.IsDefined<TEnum>(value)
82-
if (invocation.ArgumentList.Arguments.Count >= 1)
83-
{
84-
valueArgument = invocation.ArgumentList.Arguments[0];
85-
}
86-
}
87-
else if (methodSymbol.Parameters.Length == 2
88-
&& invocation.ArgumentList.Arguments.Count == 2)
89-
{
90-
// Pattern: Enum.IsDefined(typeof(TEnum), value)
91-
valueArgument = invocation.ArgumentList.Arguments[1];
92-
}
54+
ArgumentSyntax? valueArgument = null;
9355

94-
if (valueArgument is null)
95-
{
96-
continue;
97-
}
98-
99-
var type = semanticModel.Compilation.GetTypeByMetadataName(extensionTypeName);
100-
if (type is null)
56+
// Determine which argument is the value to check
57+
if (methodSymbol is { IsGenericMethod: true, TypeArguments.Length: 1 })
58+
{
59+
// Pattern: Enum.IsDefined<TEnum>(value)
60+
if (invocation.ArgumentList.Arguments.Count >= 1)
10161
{
102-
continue;
62+
valueArgument = invocation.ArgumentList.Arguments[0];
10363
}
64+
}
65+
else if (methodSymbol.Parameters.Length == 2
66+
&& invocation.ArgumentList.Arguments.Count == 2)
67+
{
68+
// Pattern: Enum.IsDefined(typeof(TEnum), value)
69+
valueArgument = invocation.ArgumentList.Arguments[1];
70+
}
10471

105-
// Create new invocation: ExtensionsClass.IsDefined(value)
106-
var newInvocation = generator.InvocationExpression(
107-
generator.MemberAccessExpression(generator.TypeExpression(type), "IsDefined"),
108-
valueArgument.Expression)
109-
.WithTriviaFrom(invocation)
110-
.WithAdditionalAnnotations(Simplifier.AddImportsAnnotation, Simplifier.Annotation);
111-
112-
editor.ReplaceNode(invocation, newInvocation);
72+
if (valueArgument is null)
73+
{
74+
return Task.CompletedTask;
11375
}
11476

115-
return editor.GetChangedDocument();
77+
// Create new invocation: ExtensionsClass.IsDefined(value)
78+
var generator = editor.Generator;
79+
var newInvocation = generator.InvocationExpression(
80+
generator.MemberAccessExpression(generator.TypeExpression(extensionTypeSymbol), "IsDefined"),
81+
valueArgument.Expression)
82+
.WithTriviaFrom(invocation)
83+
.WithAdditionalAnnotations(Simplifier.AddImportsAnnotation, Simplifier.Annotation);
84+
85+
editor.ReplaceNode(invocation, newInvocation);
86+
return Task.CompletedTask;
11687
}
117-
}
88+
}

0 commit comments

Comments
 (0)