-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
GH-153 - simplifing PartialSubstituteUsedForUnsupportedTypeCodeFixPro…
…vider
- Loading branch information
Showing
4 changed files
with
59 additions
and
65 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
85 changes: 41 additions & 44 deletions
85
...Shared/CodeFixProviders/AbstractPartialSubstituteUsedForUnsupportedTypeCodeFixProvider.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,72 +1,69 @@ | ||
using System.Collections.Immutable; | ||
using System.Linq; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.CodeActions; | ||
using Microsoft.CodeAnalysis.CodeFixes; | ||
using Microsoft.CodeAnalysis.Editing; | ||
using Microsoft.CodeAnalysis.Operations; | ||
|
||
namespace NSubstitute.Analyzers.Shared.CodeFixProviders; | ||
|
||
internal abstract class AbstractPartialSubstituteUsedForUnsupportedTypeCodeFixProvider<TInvocationExpression, TGenericNameSyntax, TIdentifierNameSyntax, TNameSyntax> | ||
: CodeFixProvider | ||
where TInvocationExpression : SyntaxNode | ||
where TGenericNameSyntax : TNameSyntax | ||
where TIdentifierNameSyntax : TNameSyntax | ||
where TNameSyntax : SyntaxNode | ||
internal abstract class AbstractPartialSubstituteUsedForUnsupportedTypeCodeFixProvider : CodeFixProvider | ||
{ | ||
public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; | ||
|
||
public override ImmutableArray<string> FixableDiagnosticIds { get; } = ImmutableArray.Create(DiagnosticIdentifiers.PartialSubstituteForUnsupportedType); | ||
public override ImmutableArray<string> FixableDiagnosticIds { get; } = | ||
ImmutableArray.Create(DiagnosticIdentifiers.PartialSubstituteForUnsupportedType); | ||
|
||
public override async Task RegisterCodeFixesAsync(CodeFixContext context) | ||
{ | ||
var diagnostic = context.Diagnostics.FirstOrDefault(diag => diag.Descriptor.Id == DiagnosticIdentifiers.PartialSubstituteForUnsupportedType); | ||
if (diagnostic != null) | ||
var diagnostic = context.Diagnostics.FirstOrDefault(diag => | ||
diag.Descriptor.Id == DiagnosticIdentifiers.PartialSubstituteForUnsupportedType); | ||
if (diagnostic == null) | ||
{ | ||
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); | ||
var invocationExpression = (TInvocationExpression)root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true); | ||
var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken); | ||
return; | ||
} | ||
|
||
if (semanticModel.GetSymbolInfo(invocationExpression).Symbol is not IMethodSymbol methodSymbol) | ||
{ | ||
return; | ||
} | ||
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); | ||
var syntaxNode = root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true); | ||
var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken); | ||
|
||
var title = methodSymbol.Name == MetadataNames.SubstituteFactoryCreatePartial ? "Use SubstituteFactory.Create" : "Use Substitute.For"; | ||
var codeAction = CodeAction.Create( | ||
title, | ||
ct => CreateChangedDocument(context, root, methodSymbol, invocationExpression), | ||
nameof(AbstractPartialSubstituteUsedForUnsupportedTypeCodeFixProvider<TInvocationExpression, TGenericNameSyntax, TIdentifierNameSyntax, TNameSyntax>)); | ||
context.RegisterCodeFix(codeAction, diagnostic); | ||
if (semanticModel.GetOperation(syntaxNode) is not IInvocationOperation invocationOperation) | ||
{ | ||
return; | ||
} | ||
} | ||
|
||
protected abstract TInnerNameSyntax GetNameSyntax<TInnerNameSyntax>(TInvocationExpression methodInvocationNode) where TInnerNameSyntax : TNameSyntax; | ||
var title = invocationOperation.TargetMethod.Name == MetadataNames.SubstituteFactoryCreatePartial | ||
? "Use SubstituteFactory.Create" | ||
: "Use Substitute.For"; | ||
|
||
protected abstract TInnerNameSyntax GetUpdatedNameSyntax<TInnerNameSyntax>(TInnerNameSyntax nameSyntax, string identifierName) where TInnerNameSyntax : TNameSyntax; | ||
var codeAction = CodeAction.Create( | ||
title, | ||
ct => CreateChangedDocument(context, invocationOperation, ct), | ||
nameof(AbstractPartialSubstituteUsedForUnsupportedTypeCodeFixProvider)); | ||
context.RegisterCodeFix(codeAction, diagnostic); | ||
} | ||
|
||
private Task<Document> CreateChangedDocument(CodeFixContext context, SyntaxNode root, IMethodSymbol methodSymbol, TInvocationExpression invocationExpression) | ||
{ | ||
SyntaxNode nameNode; | ||
SyntaxNode updateNameNode; | ||
protected abstract SyntaxNode UpdateInvocationExpression( | ||
IInvocationOperation invocationOperation, | ||
string identifierName); | ||
|
||
if (methodSymbol.IsGenericMethod) | ||
{ | ||
var genericNameSyntax = GetNameSyntax<TGenericNameSyntax>(invocationExpression); | ||
nameNode = genericNameSyntax; | ||
updateNameNode = GetUpdatedNameSyntax(genericNameSyntax, MetadataNames.NSubstituteForMethod); | ||
} | ||
else | ||
{ | ||
var identifierNameSyntax = GetNameSyntax<TIdentifierNameSyntax>(invocationExpression); | ||
nameNode = identifierNameSyntax; | ||
updateNameNode = GetUpdatedNameSyntax(identifierNameSyntax, MetadataNames.SubstituteFactoryCreate); | ||
} | ||
private async Task<Document> CreateChangedDocument( | ||
CodeFixContext context, | ||
IInvocationOperation invocationOperation, | ||
CancellationToken cancellationToken) | ||
{ | ||
var documentEditor = await DocumentEditor.CreateAsync(context.Document, cancellationToken); | ||
var newIdentifierName = invocationOperation.TargetMethod.IsGenericMethod | ||
? MetadataNames.NSubstituteForMethod | ||
: MetadataNames.SubstituteFactoryCreate; | ||
|
||
var forNode = invocationExpression.ReplaceNode(nameNode, updateNameNode); | ||
var updatedInvocationExpression = UpdateInvocationExpression(invocationOperation, newIdentifierName); | ||
|
||
var replaceNode = root.ReplaceNode(invocationExpression, forNode); | ||
documentEditor.ReplaceNode(invocationOperation.Syntax, updatedInvocationExpression); | ||
|
||
return Task.FromResult(context.Document.WithSyntaxRoot(replaceNode)); | ||
return documentEditor.GetChangedDocument(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters