Skip to content

Commit

Permalink
[GH-153] - simplifing PartialSubstituteUsedForUnsupportedTypeCodeFixP…
Browse files Browse the repository at this point in the history
…rovider
  • Loading branch information
tpodolak committed Dec 20, 2020
1 parent 3876efb commit 47aa1e7
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@
namespace NSubstitute.Analyzers.CSharp.CodeFixProviders
{
[ExportCodeFixProvider(LanguageNames.CSharp)]
internal sealed class PartialSubstituteUsedForUnsupportedTypeCodeFixProvider : AbstractPartialSubstituteUsedForUnsupportedTypeCodeFixProvider<InvocationExpressionSyntax, GenericNameSyntax, IdentifierNameSyntax, SimpleNameSyntax>
internal sealed class PartialSubstituteUsedForUnsupportedTypeCodeFixProvider : AbstractPartialSubstituteUsedForUnsupportedTypeCodeFixProvider<InvocationExpressionSyntax, SimpleNameSyntax>
{
protected override TInnerNameSyntax GetNameSyntax<TInnerNameSyntax>(InvocationExpressionSyntax methodInvocationNode)
protected override SimpleNameSyntax GetNameSyntax(InvocationExpressionSyntax methodInvocationNode)
{
var memberAccess = (MemberAccessExpressionSyntax)methodInvocationNode.Expression;
return (TInnerNameSyntax)memberAccess.Name;
return memberAccess.Name;
}

protected override TInnerNameSyntax GetUpdatedNameSyntax<TInnerNameSyntax>(TInnerNameSyntax nameSyntax, string identifierName)
protected override SimpleNameSyntax GetUpdatedNameSyntax(SimpleNameSyntax nameSyntax, string identifierName)
{
return (TInnerNameSyntax)nameSyntax.WithIdentifier(IdentifierName(identifierName).Identifier);
return nameSyntax.WithIdentifier(IdentifierName(identifierName).Identifier);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.Editing;

namespace NSubstitute.Analyzers.Shared.CodeFixProviders
{
internal abstract class AbstractPartialSubstituteUsedForUnsupportedTypeCodeFixProvider<TInvocationExpression, TGenericNameSyntax, TIdentifierNameSyntax, TNameSyntax>
internal abstract class AbstractPartialSubstituteUsedForUnsupportedTypeCodeFixProvider<TInvocationExpression, TSimpleNameSyntax>
: CodeFixProvider
where TInvocationExpression : SyntaxNode
where TGenericNameSyntax : TNameSyntax
where TIdentifierNameSyntax : TNameSyntax
where TNameSyntax : SyntaxNode
where TSimpleNameSyntax : SyntaxNode
{
public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;

Expand All @@ -21,53 +20,51 @@ internal abstract class AbstractPartialSubstituteUsedForUnsupportedTypeCodeFixPr
public override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
var diagnostic = context.Diagnostics.FirstOrDefault(diag => diag.Descriptor.Id == DiagnosticIdentifiers.PartialSubstituteForUnsupportedType);
if (diagnostic != null)
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 IMethodSymbol methodSymbol))
{
return;
}
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);

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.GetSymbolInfo(invocationExpression).Symbol is IMethodSymbol methodSymbol))
{
return;
}

var title = methodSymbol.Name == MetadataNames.SubstituteFactoryCreatePartial
? "Use SubstituteFactory.Create"
: "Use Substitute.For";

var codeAction = CodeAction.Create(
title,
ct => CreateChangedDocument(context, methodSymbol, invocationExpression),
nameof(AbstractPartialSubstituteUsedForUnsupportedTypeCodeFixProvider<TInvocationExpression, TSimpleNameSyntax>));

context.RegisterCodeFix(codeAction, diagnostic);
}

protected abstract TInnerNameSyntax GetNameSyntax<TInnerNameSyntax>(TInvocationExpression methodInvocationNode) where TInnerNameSyntax : TNameSyntax;
protected abstract TSimpleNameSyntax GetNameSyntax(TInvocationExpression methodInvocationNode);

protected abstract TInnerNameSyntax GetUpdatedNameSyntax<TInnerNameSyntax>(TInnerNameSyntax nameSyntax, string identifierName) where TInnerNameSyntax : TNameSyntax;
protected abstract TSimpleNameSyntax GetUpdatedNameSyntax(TSimpleNameSyntax nameSyntax, string identifierName);

private Task<Document> CreateChangedDocument(CodeFixContext context, SyntaxNode root, IMethodSymbol methodSymbol, TInvocationExpression invocationExpression)
private async Task<Document> CreateChangedDocument(CodeFixContext context, IMethodSymbol methodSymbol, TInvocationExpression invocationExpression)
{
SyntaxNode nameNode;
SyntaxNode updateNameNode;
var documentEditor = await DocumentEditor.CreateAsync(context.Document);
var newIdentifierName = methodSymbol.IsGenericMethod
? MetadataNames.NSubstituteForMethod
: MetadataNames.SubstituteFactoryCreate;

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);
}
var nameNode = GetNameSyntax(invocationExpression);
var updateNameNode = GetUpdatedNameSyntax(nameNode, newIdentifierName);

var forNode = invocationExpression.ReplaceNode(nameNode, updateNameNode);
var updatedInvocationExpression = invocationExpression.ReplaceNode(nameNode, updateNameNode);

var replaceNode = root.ReplaceNode(invocationExpression, forNode);
documentEditor.ReplaceNode(invocationExpression, updatedInvocationExpression);

return Task.FromResult(context.Document.WithSyntaxRoot(replaceNode));
return documentEditor.GetChangedDocument();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public static bool IsEventAssignmentOperation(this IOperation operation)
public static IEnumerable<IArgumentOperation> GetOrderedArgumentOperationsWithoutInstanceArgument(this IInvocationOperation invocationOperation)
{
var orderedArguments = invocationOperation.GetOrderedArgumentOperations()
.Where(arg => IsImplicitlyProvidedArrayWithValues(arg) == false);
.Where(arg => IsImplicitlyProvidedArrayWithoutValues(arg) == false);

if (!invocationOperation.TargetMethod.IsExtensionMethod)
{
Expand Down Expand Up @@ -109,7 +109,7 @@ public static IOperation GetSubstituteOperation(this IInvocationOperation invoca
return (int)literal.ConstantValue.Value;
}

private static bool IsImplicitlyProvidedArrayWithValues(IArgumentOperation arg)
private static bool IsImplicitlyProvidedArrayWithoutValues(IArgumentOperation arg)
{
return arg.IsImplicit &&
arg.ArgumentKind == ArgumentKind.ParamArray &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@
namespace NSubstitute.Analyzers.VisualBasic.CodeFixProviders
{
[ExportCodeFixProvider(LanguageNames.VisualBasic)]
internal sealed class PartialSubstituteUsedForUnsupportedTypeCodeFixProvider : AbstractPartialSubstituteUsedForUnsupportedTypeCodeFixProvider<InvocationExpressionSyntax, GenericNameSyntax, IdentifierNameSyntax, SimpleNameSyntax>
internal sealed class PartialSubstituteUsedForUnsupportedTypeCodeFixProvider : AbstractPartialSubstituteUsedForUnsupportedTypeCodeFixProvider<InvocationExpressionSyntax, SimpleNameSyntax>
{
protected override TInnerNameSyntax GetNameSyntax<TInnerNameSyntax>(InvocationExpressionSyntax methodInvocationNode)
protected override SimpleNameSyntax GetNameSyntax(InvocationExpressionSyntax methodInvocationNode)
{
var memberAccess = (MemberAccessExpressionSyntax)methodInvocationNode.Expression;
return memberAccess.Name as TInnerNameSyntax;
return memberAccess.Name;
}

protected override TInnerNameSyntax GetUpdatedNameSyntax<TInnerNameSyntax>(TInnerNameSyntax nameSyntax, string identifierName)
protected override SimpleNameSyntax GetUpdatedNameSyntax(SimpleNameSyntax nameSyntax, string identifierName)
{
return (TInnerNameSyntax)nameSyntax.WithIdentifier(IdentifierName(identifierName).Identifier);
return nameSyntax.WithIdentifier(IdentifierName(identifierName).Identifier);
}
}
}

0 comments on commit 47aa1e7

Please sign in to comment.