Skip to content

Commit

Permalink
[GH-153] - reentrant tests
Browse files Browse the repository at this point in the history
  • Loading branch information
tpodolak committed Dec 20, 2020
1 parent 2dd78b3 commit 3cabac1
Show file tree
Hide file tree
Showing 8 changed files with 72 additions and 145 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using Microsoft.CodeAnalysis.Simplification;
using NSubstitute.Analyzers.CSharp.Extensions;
using NSubstitute.Analyzers.Shared.CodeFixProviders;
using NSubstitute.Analyzers.Shared.Extensions;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;

namespace NSubstitute.Analyzers.CSharp.CodeFixProviders
Expand All @@ -31,41 +32,17 @@ protected override IEnumerable<SyntaxNode> GetParameterExpressionsFromArrayArgum
protected override ArgumentSyntax CreateUpdatedParamsArgumentSyntaxNode(
SyntaxGenerator syntaxGenerator,
ITypeSymbol typeSymbol,
ArgumentSyntax argumentSyntaxNode)
ArgumentSyntax argumentSyntaxNode,
IEnumerable<SyntaxNode> initializers)
{
var expression = argumentSyntaxNode.Expression;
ArrayCreationExpressionSyntax resultArrayCreationExpressionSyntax;
var arrayCreationExpression = CreateArrayCreationExpression(syntaxGenerator, typeSymbol, initializers);

switch (expression)
{
case ArrayCreationExpressionSyntax arrayCreationExpressionSyntax:
resultArrayCreationExpressionSyntax = CreateArrayCreationExpression(
syntaxGenerator,
typeSymbol,
arrayCreationExpressionSyntax.Initializer);
break;
case ImplicitArrayCreationExpressionSyntax implicitArrayCreationExpressionSyntax:
resultArrayCreationExpressionSyntax = CreateArrayCreationExpression(
syntaxGenerator,
typeSymbol,
implicitArrayCreationExpressionSyntax.Initializer);
break;
default:
throw new ArgumentException($"{argumentSyntaxNode.Kind()} is not recognized as array initialization", nameof(argumentSyntaxNode));
}

return argumentSyntaxNode.WithExpression(resultArrayCreationExpressionSyntax);
return argumentSyntaxNode.WithExpression(arrayCreationExpression);
}

protected override IEnumerable<ArgumentSyntax> GetArguments(ArgumentListSyntax argumentSyntax)
{
return argumentSyntax.Arguments;
}
protected override IEnumerable<ArgumentSyntax> GetArguments(ArgumentListSyntax argumentSyntax) => argumentSyntax.Arguments;

protected override SyntaxNode GetArgumentExpressionSyntax(ArgumentSyntax argumentSyntax)
{
return argumentSyntax.Expression;
}
protected override SyntaxNode GetArgumentExpressionSyntax(ArgumentSyntax argumentSyntax) => argumentSyntax.Expression;

private static SimpleLambdaExpressionSyntax CreateSimpleLambdaExpressionNode(SyntaxNode content)
{
Expand All @@ -77,43 +54,28 @@ private static SimpleLambdaExpressionSyntax CreateSimpleLambdaExpressionNode(Syn
private static ArrayCreationExpressionSyntax CreateArrayCreationExpression(
SyntaxGenerator syntaxGenerator,
ITypeSymbol typeSymbol,
InitializerExpressionSyntax initializerExpressionSyntax)
IEnumerable<SyntaxNode> initializers)
{
var arrayType = CreateArrayTypeNode(syntaxGenerator, typeSymbol);
var syntaxes = CreateSimpleLambdaExpressions(initializerExpressionSyntax);
var syntaxes = CreateSimpleLambdaExpressions(initializers);

var initializer = InitializerExpression(SyntaxKind.ArrayInitializerExpression, syntaxes);

return ArrayCreationExpression(arrayType, initializer);
}

private static SeparatedSyntaxList<ExpressionSyntax> CreateSimpleLambdaExpressions(InitializerExpressionSyntax initializerExpressionSyntax)
private static SeparatedSyntaxList<ExpressionSyntax> CreateSimpleLambdaExpressions(IEnumerable<SyntaxNode> initializers)
{
var expressions = initializerExpressionSyntax.Expressions.Select<ExpressionSyntax, ExpressionSyntax>(CreateSimpleLambdaExpressionNode);
var expressions = initializers.Select<SyntaxNode, ExpressionSyntax>(CreateSimpleLambdaExpressionNode);
return SeparatedList(expressions);
}

private static ArrayTypeSyntax CreateArrayTypeNode(SyntaxGenerator syntaxGenerator, ITypeSymbol type)
{
var typeSyntax = (TypeSyntax)syntaxGenerator.TypeExpression(type);
var typeArgumentListSyntax = TypeArgumentList(
SeparatedList<TypeSyntax>(
new SyntaxNodeOrToken[]
{
QualifiedName(
QualifiedName(
IdentifierName("NSubstitute"),
IdentifierName("Core")),
IdentifierName("CallInfo")),
Token(SyntaxKind.CommaToken),
typeSyntax
}));

var qualifiedNameSyntax = QualifiedName(IdentifierName("System"), GenericName(Identifier("Func"), typeArgumentListSyntax));

var callbackTypeSyntax = syntaxGenerator.CallInfoCallbackTypeSyntax(type);
var arrayRankSpecifierSyntaxes = SingletonList(ArrayRankSpecifier(SingletonSeparatedList<ExpressionSyntax>(OmittedArraySizeExpression())));

return ArrayType(qualifiedNameSyntax, arrayRankSpecifierSyntaxes).WithAdditionalAnnotations(Simplifier.Annotation);
return ArrayType((TypeSyntax)callbackTypeSyntax, arrayRankSpecifierSyntaxes).WithAdditionalAnnotations(Simplifier.Annotation);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ internal abstract class AbstractReEntrantSetupCodeFixProvider<TArgumentListSynta

protected abstract TArgumentSyntax CreateUpdatedArgumentSyntaxNode(TArgumentSyntax argumentSyntaxNode);

protected abstract TArgumentSyntax CreateUpdatedParamsArgumentSyntaxNode(SyntaxGenerator syntaxGenerator, ITypeSymbol returnedTypeSymbol, TArgumentSyntax argumentSyntaxNode);
protected abstract TArgumentSyntax CreateUpdatedParamsArgumentSyntaxNode(SyntaxGenerator syntaxGenerator, ITypeSymbol returnedTypeSymbol, TArgumentSyntax argumentSyntaxNode, IEnumerable<SyntaxNode> initializers);

protected abstract SyntaxNode GetArgumentExpressionSyntax(TArgumentSyntax argumentSyntax);

Expand Down Expand Up @@ -74,18 +74,23 @@ private async Task<Document> CreateChangedDocument(
return context.Document;
}

var skip = methodSymbol.MethodKind == MethodKind.Ordinary
? 1
: 0;

foreach (var argumentSyntax in argumentSyntaxes.Skip(skip))
var syntaxGenerator = SyntaxGenerator.GetGenerator(context.Document);
foreach (var argumentSyntax in argumentSyntaxes)
{
if (IsArrayParamsArgument(semanticModel, argumentSyntax))
var operation = semanticModel.GetOperation(argumentSyntax) as IArgumentOperation;
if (operation != null && (methodSymbol.MethodKind == MethodKind.Ordinary && operation.Parameter.Ordinal == 0))
{
continue;
}

if (IsArrayParamsArgument(operation))
{
var initializers = GetInitializers(operation);
var updatedParamsArgumentSyntaxNode = CreateUpdatedParamsArgumentSyntaxNode(
SyntaxGenerator.GetGenerator(context.Document),
syntaxGenerator,
methodSymbol.TypeArguments.FirstOrDefault() ?? methodSymbol.ReceiverType,
argumentSyntax);
argumentSyntax,
initializers);

documentEditor.ReplaceNode(argumentSyntax, updatedParamsArgumentSyntaxNode);
}
Expand Down Expand Up @@ -115,13 +120,16 @@ private bool IsFixSupported(SemanticModel semanticModel, IEnumerable<TArgumentSy
private bool IsArrayParamsArgument(SemanticModel semanticModel, TArgumentSyntax argumentSyntax)
{
var operation = semanticModel.GetOperation(argumentSyntax);
return operation is IArgumentOperation argumentOperation && argumentOperation.Parameter.IsParams;
return operation is IArgumentOperation argumentOperation && IsArrayParamsArgument(argumentOperation);
}

private static TArgumentListSyntax GetArgumentListSyntax(SyntaxNode diagnosticNode)
private bool IsArrayParamsArgument(IArgumentOperation argumentOperation) => argumentOperation != null && argumentOperation.Parameter.IsParams;

private IEnumerable<SyntaxNode> GetInitializers(IArgumentOperation argumentOperation)
{
var argumentListSyntax = diagnosticNode.Ancestors().OfType<TArgumentListSyntax>().FirstOrDefault();
return argumentListSyntax;
return (argumentOperation.Value as IArrayCreationOperation).Initializer.ElementValues.Select(value => value.Syntax);
}

private static TArgumentListSyntax GetArgumentListSyntax(SyntaxNode diagnosticNode) => diagnosticNode.Ancestors().OfType<TArgumentListSyntax>().FirstOrDefault();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,26 +40,6 @@ public static IEnumerable<IArgumentOperation> GetOrderedArgumentOperationsWithou
return orderedArguments.Skip(1);
}

public static IEnumerable<IArgumentOperation> GetArgumentOperationsWithoutInstanceArgument(this IInvocationOperation invocationOperation)
{
var orderedArguments = invocationOperation.Arguments
.Where(arg => IsImplicitlyProvidedArrayWithoutValues(arg) == false);

if (!invocationOperation.TargetMethod.IsExtensionMethod)
{
return orderedArguments;
}

// unlike CSharp implementation, VisualBasic doesnt include "instance" argument for reduced extensions
if (invocationOperation.TargetMethod.MethodKind == MethodKind.ReducedExtension &&
invocationOperation.Language == LanguageNames.VisualBasic)
{
return orderedArguments;
}

return orderedArguments.Where(x => x.Parameter.Ordinal != 0);
}

public static IEnumerable<IArgumentOperation> GetOrderedArgumentOperations(this IInvocationOperation invocationOperation)
{
return invocationOperation.Arguments.OrderBy(arg => arg.Parameter.Ordinal);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Simplification;

namespace NSubstitute.Analyzers.Shared.Extensions
{
Expand All @@ -9,9 +10,7 @@ public static SyntaxNode SubstituteForInvocationExpression(
this SyntaxGenerator syntaxGenerator,
IParameterSymbol parameterSymbol)
{
var qualifiedName = syntaxGenerator.QualifiedName(
syntaxGenerator.IdentifierName("NSubstitute"),
syntaxGenerator.IdentifierName("Substitute"));
var qualifiedName = syntaxGenerator.DottedName("NSubstitute.Substitute");

var genericName = syntaxGenerator.GenericName("For", syntaxGenerator.TypeExpression(parameterSymbol.Type));

Expand All @@ -22,6 +21,17 @@ public static SyntaxNode SubstituteForInvocationExpression(
return invocationExpression;
}

public static SyntaxNode CallInfoCallbackTypeSyntax(
this SyntaxGenerator syntaxGenerator,
ITypeSymbol returnedType)
{
var typeSyntax = syntaxGenerator.TypeExpression(returnedType);
var genericName = syntaxGenerator.GenericName("Func", syntaxGenerator.DottedName("NSubstitute.Core.CallInfo"), typeSyntax);
var qualifiedNameSyntax = syntaxGenerator.QualifiedName(syntaxGenerator.IdentifierName("System"), genericName);

return qualifiedNameSyntax.WithAdditionalAnnotations(Simplifier.Annotation);
}

public static SyntaxNode InternalVisibleToDynamicProxyAttributeList(this SyntaxGenerator syntaxGenerator)
{
var attributeArguments =
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Simplification;
using Microsoft.CodeAnalysis.VisualBasic;
using Microsoft.CodeAnalysis.VisualBasic.Syntax;
using NSubstitute.Analyzers.Shared.CodeFixProviders;
using NSubstitute.Analyzers.Shared.Extensions;
using NSubstitute.Analyzers.VisualBasic.Extensions;
using static Microsoft.CodeAnalysis.VisualBasic.SyntaxFactory;

Expand All @@ -31,41 +30,22 @@ protected override ArgumentSyntax CreateUpdatedArgumentSyntaxNode(ArgumentSyntax
return argumentSyntaxNode;
}

protected override ArgumentSyntax CreateUpdatedParamsArgumentSyntaxNode(SyntaxGenerator syntaxGenerator, ITypeSymbol typeSymbol, ArgumentSyntax argumentSyntaxNode)
protected override ArgumentSyntax CreateUpdatedParamsArgumentSyntaxNode(
SyntaxGenerator syntaxGenerator,
ITypeSymbol typeSymbol,
ArgumentSyntax argumentSyntaxNode,
IEnumerable<SyntaxNode> initializers)
{
if (!(argumentSyntaxNode is SimpleArgumentSyntax simpleArgumentSyntax))
{
return argumentSyntaxNode;
}

var expression = argumentSyntaxNode.GetExpression();
ArrayCreationExpressionSyntax resultArrayCreationExpressionSyntax;

switch (expression)
{
case ArrayCreationExpressionSyntax arrayCreationExpressionSyntax:
resultArrayCreationExpressionSyntax = CreateArrayCreationExpression(
syntaxGenerator,
typeSymbol,
arrayCreationExpressionSyntax.Initializer.Initializers);
break;
case CollectionInitializerSyntax implicitArrayCreationExpressionSyntax:
resultArrayCreationExpressionSyntax = CreateArrayCreationExpression(
syntaxGenerator,
typeSymbol,
implicitArrayCreationExpressionSyntax.Initializers);
break;
default:
throw new ArgumentException($"{argumentSyntaxNode.Kind()} is not recognized as array initialization", nameof(argumentSyntaxNode));
}

return simpleArgumentSyntax.WithExpression(resultArrayCreationExpressionSyntax);
var arrayCreationExpression = CreateArrayCreationExpression(syntaxGenerator, typeSymbol, initializers);
return simpleArgumentSyntax.WithExpression(arrayCreationExpression);
}

protected override SyntaxNode GetArgumentExpressionSyntax(ArgumentSyntax argumentSyntax)
{
return argumentSyntax.GetExpression();
}
protected override SyntaxNode GetArgumentExpressionSyntax(ArgumentSyntax argumentSyntax) => argumentSyntax.GetExpression();

protected override IEnumerable<SyntaxNode> GetParameterExpressionsFromArrayArgument(ArgumentSyntax argumentSyntaxNode)
{
Expand All @@ -74,23 +54,21 @@ protected override IEnumerable<SyntaxNode> GetParameterExpressionsFromArrayArgum

protected override int AwaitExpressionRawKind { get; } = (int)SyntaxKind.AwaitExpression;

protected override IEnumerable<ArgumentSyntax> GetArguments(ArgumentListSyntax argumentSyntax)
{
return argumentSyntax.Arguments;
}
protected override IEnumerable<ArgumentSyntax> GetArguments(ArgumentListSyntax argumentSyntax) => argumentSyntax.Arguments;

private static ArrayCreationExpressionSyntax CreateArrayCreationExpression(
SyntaxGenerator syntaxGenerator,
ITypeSymbol typeSymbol,
SeparatedSyntaxList<ExpressionSyntax> initializers)
IEnumerable<SyntaxNode> initializers)
{
var typeNode = CreateTypeNode(syntaxGenerator, typeSymbol);
var syntaxes = CreateSingleLineLambdaExpressions(initializers);
var initializersSyntaxList = SeparatedList(initializers);
var typeNode = syntaxGenerator.CallInfoCallbackTypeSyntax(typeSymbol);
var syntaxes = CreateSingleLineLambdaExpressions(initializersSyntaxList);

var initializer = CollectionInitializer(syntaxes);
var arrayRankSpecifierSyntaxes = SingletonList(ArrayRankSpecifier());

return ArrayCreationExpression(Token(SyntaxKind.NewKeyword), new SyntaxList<AttributeListSyntax>(), typeNode, null, arrayRankSpecifierSyntaxes, initializer);
return ArrayCreationExpression(Token(SyntaxKind.NewKeyword), new SyntaxList<AttributeListSyntax>(), (TypeSyntax)typeNode, null, arrayRankSpecifierSyntaxes, initializer);
}

private static SeparatedSyntaxList<ExpressionSyntax> CreateSingleLineLambdaExpressions(SeparatedSyntaxList<ExpressionSyntax> expressions)
Expand All @@ -115,26 +93,5 @@ private static SingleLineLambdaExpressionSyntax CreateSingleLineLambdaExpression
expressionSyntax.WithLeadingTrivia());
return lambdaExpression;
}

private static QualifiedNameSyntax CreateTypeNode(SyntaxGenerator syntaxGenerator, ITypeSymbol type)
{
var typeSyntax = (TypeSyntax)syntaxGenerator.TypeExpression(type);
var typeArgumentListSyntax = TypeArgumentList(
SeparatedList<TypeSyntax>(
new SyntaxNodeOrToken[]
{
QualifiedName(
QualifiedName(
IdentifierName("NSubstitute"),
IdentifierName("Core")),
IdentifierName("CallInfo")),
Token(SyntaxKind.CommaToken),
typeSyntax
}));

var qualifiedNameSyntax = QualifiedName(IdentifierName("System"), GenericName(Identifier("Func"), typeArgumentListSyntax));

return qualifiedNameSyntax.WithAdditionalAnnotations(Simplifier.Annotation);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ public void Test()
{
var secondSubstitute = Substitute.For<IFoo>();
secondSubstitute.Id.Returns(CreateReEntrantSubstitute(), new[] { MyNamespace.FooTests.Value });
secondSubstitute.Id.Returns(returnThis: CreateReEntrantSubstitute(), returnThese: new[] { MyNamespace.FooTests.Value });
}
private int CreateReEntrantSubstitute()
Expand Down Expand Up @@ -149,6 +150,7 @@ public void Test()
{
var secondSubstitute = Substitute.For<IFoo>();
secondSubstitute.Id.Returns(_ => CreateReEntrantSubstitute(), new Func<CallInfo, int>[] { _ => MyNamespace.FooTests.Value });
secondSubstitute.Id.Returns(returnThis: _ => CreateReEntrantSubstitute(), returnThese: new Func<CallInfo, int>[] { _ => MyNamespace.FooTests.Value });
}
private int CreateReEntrantSubstitute()
Expand Down
Loading

0 comments on commit 3cabac1

Please sign in to comment.