Skip to content

Commit

Permalink
[GH-1] - adapting to the dev changes - first stage
Browse files Browse the repository at this point in the history
  • Loading branch information
tpodolak committed Jun 9, 2018
1 parent 4ce8f6d commit f691234
Show file tree
Hide file tree
Showing 34 changed files with 702 additions and 341 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using NSubstitute.Analyzers.Shared.DiagnosticAnalyzers;

namespace NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers
{
internal class SubstituteAnalyzer : AbstractSubstituteAnalyzer<SyntaxKind, InvocationExpressionSyntax, ExpressionSyntax>
{
public SubstituteAnalyzer() : base(new DiagnosticDescriptorsProvider())
{
}

protected override SyntaxKind InvocationExpressionKind { get; } = SyntaxKind.InvocationExpression;

protected override IEnumerable<ExpressionSyntax> GetTypeOfLikeExpressions(IList<ExpressionSyntax> arrayParameters)
{
return arrayParameters.OfType<TypeOfExpressionSyntax>();
}

protected override ConstructorContext CollectConstructorContext(SubstituteContext substituteContext, ITypeSymbol proxyTypeSymbol)
{
throw new System.NotImplementedException();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,21 @@ internal class AbstractDiagnosticDescriptorsProvider<T> : IDiagnosticDescriptors
public DiagnosticDescriptor UnusedReceived { get; } = DiagnosticDescriptors<T>.UnusedReceived;

public DiagnosticDescriptor UnusedReceivedForOrdinaryMethod { get; } = DiagnosticDescriptors<T>.UnusedReceivedForOrdinaryMethod;

public DiagnosticDescriptor SubstituteForPartsOfUsedForInterface { get; } = DiagnosticDescriptors<T>.SubstituteForPartsOfUsedForInterface;

public DiagnosticDescriptor SubstituteForWithoutAccessibleConstructor { get; } = DiagnosticDescriptors<T>.SubstituteForWithoutAccessibleConstructor;

public DiagnosticDescriptor SubstituteForConstructorParametersMismatch { get; } = DiagnosticDescriptors<T>.SubstituteForConstructorParametersMismatch;

public DiagnosticDescriptor SubstituteForInternalMember { get; } = DiagnosticDescriptors<T>.SubstituteForInternalMember;

public DiagnosticDescriptor SubstituteConstructorMismatch { get; } = DiagnosticDescriptors<T>.SubstituteConstructorMismatch;

public DiagnosticDescriptor SubstituteMultipleClasses { get; } = DiagnosticDescriptors<T>.SubstituteMultipleClasses;

public DiagnosticDescriptor SubstituteConstructorArgumentsForInterface { get; } = DiagnosticDescriptors<T>.SubstituteConstructorArgumentsForInterface;

public DiagnosticDescriptor SubstituteConstructorArgumentsForDelegate { get; } = DiagnosticDescriptors<T>.SubstituteConstructorArgumentsForDelegate;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using System.Collections.Immutable;
using System.Linq;
using System.Reflection.Metadata;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;

namespace NSubstitute.Analyzers.Shared.CodeFixProviders
{
#if CSHARP
[ExportCodeFixProvider(LanguageNames.CSharp)]
#elif VISUAL_BASIC
[ExportCodeFixProvider(LanguageNames.VisualBasic)]
#endif
public class AbstractConstructorArgumentsForInterfaceCodeFixProvider : CodeFixProvider
{
public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;

public override ImmutableArray<string> FixableDiagnosticIds { get; } = ImmutableArray.Create(DiagnosticIdentifiers.SubstituteConstructorArgumentsForInterface);

public override Task RegisterCodeFixesAsync(CodeFixContext context)
{
var diagnostic = context.Diagnostics.FirstOrDefault(diag => diag.Descriptor.Id == DiagnosticIdentifiers.SubstituteConstructorArgumentsForInterface);
if (diagnostic != null)
{
var codeAction = CodeAction.Create("Use Substitute.For", ct => CreateChangedDocument(ct, context, diagnostic), "equvalencyKey");
context.RegisterCodeFix(codeAction, diagnostic);
}

return Task.CompletedTask;
}

private async Task<Document> CreateChangedDocument(CancellationToken cancellationToken, CodeFixContext context, Diagnostic diagnostic)
{
var root = await context.Document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

var invocation = (InvocationExpressionSyntax)root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true);
var semanticModel = await context.Document.GetSemanticModelAsync(cancellationToken);
ArgumentListSyntax argumentListSyntax;
if (semanticModel.GetSymbolInfo(invocation).Symbol is IMethodSymbol methodSymbol && methodSymbol.IsGenericMethod)
{
argumentListSyntax = ArgumentList();
}
else
{
// TODO consider making the vb and c# analyzers/fixproviders independent
#if CSHARP
var nullSyntax = Argument(LiteralExpression(SyntaxKind.NullLiteralExpression));
var seconArgument = invocation.ArgumentList.Arguments.Skip(1).First();
argumentListSyntax = invocation.ArgumentList.ReplaceNode(seconArgument, nullSyntax);
#elif VISUAL_BASIC
var nullSyntax = SimpleArgument(LiteralExpression(SyntaxKind.NothingLiteralExpression, Token(SyntaxKind.NothingKeyword)));
var seconArgument = invocation.ArgumentList.Arguments.Skip(1).First();
argumentListSyntax = invocation.ArgumentList.ReplaceNode(seconArgument, nullSyntax);
#endif
}

var updatedInvocation = invocation.WithArgumentList(argumentListSyntax);
var replacedRoot = root.ReplaceNode(invocation, updatedInvocation);
return context.Document.WithSyntaxRoot(replacedRoot);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
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;
#if CSHARP
using Microsoft.CodeAnalysis.CSharp.Syntax;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
#elif VISUAL_BASIC
using Microsoft.CodeAnalysis.VisualBasic.Syntax;
using static Microsoft.CodeAnalysis.VisualBasic.SyntaxFactory;
#endif

namespace NSubstitute.Analyzers.Shared.CodeFixProviders
{
#if CSHARP
[ExportCodeFixProvider(LanguageNames.CSharp)]
#elif VISUAL_BASIC
[ExportCodeFixProvider(LanguageNames.VisualBasic)]
#endif
public class AbstractForPartsOfUsedForUnsupportedTypeCodeFixProvider : CodeFixProvider
{
// no completed task in .net standard
private static Task _completedTask = Task.FromResult(1);

public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;

public override ImmutableArray<string> FixableDiagnosticIds { get; } =
ImmutableArray.Create(DiagnosticIdentifiers.SubstituteForPartsOfUsedForInterface);

public override Task RegisterCodeFixesAsync(CodeFixContext context)
{
var diagnostic = context.Diagnostics.FirstOrDefault(diag => diag.Descriptor.Id == DiagnosticIdentifiers.SubstituteForPartsOfUsedForInterface);
if (diagnostic != null)
{
var codeAction = CodeAction.Create("Use Substitute.For", ct => CreateChangedDocument(ct, context, diagnostic), "equvalencyKey");
context.RegisterCodeFix(codeAction, diagnostic);
}

return _completedTask;
}

private async Task<Document> CreateChangedDocument(CancellationToken cancellationToken, CodeFixContext context, Diagnostic diagnostic)
{
var root = await context.Document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

var forPartsOfNode = (InvocationExpressionSyntax)root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true);
var nameNode = GetGenericNameSyntax(forPartsOfNode);
var forNode = forPartsOfNode.ReplaceNode(nameNode, nameNode.WithIdentifier(IdentifierName("For").Identifier));

var replaceNode = root.ReplaceNode(forPartsOfNode, forNode);

return context.Document.WithSyntaxRoot(replaceNode);
}

private static GenericNameSyntax GetGenericNameSyntax(InvocationExpressionSyntax methodInvocationNode)
{
var memberAccess = (MemberAccessExpressionSyntax)methodInvocationNode.Expression;
return (GenericNameSyntax)memberAccess.Name;
}
}
}
Loading

0 comments on commit f691234

Please sign in to comment.