Skip to content

Commit

Permalink
[GH-147] - proper handling of event assignments for Arg.Any like methods
Browse files Browse the repository at this point in the history
  • Loading branch information
tpodolak committed Sep 13, 2020
1 parent cd8b52d commit c09694d
Show file tree
Hide file tree
Showing 9 changed files with 90 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ internal sealed class NonSubstitutableMemberArgumentMatcherAnalyzer : AbstractNo
(int)SyntaxKind.AsExpression,
(int)SyntaxKind.Argument,
(int)SyntaxKind.BracketedArgumentList,
(int)SyntaxKind.ElementAccessExpression));
(int)SyntaxKind.ElementAccessExpression),
ImmutableArray.Create((int)SyntaxKind.AddAssignmentExpression));

private static ImmutableArray<ImmutableArray<int>> IgnoredPaths { get; } = ImmutableArray.Create(
ImmutableArray.Create(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,21 +64,31 @@ private void AnalyzeArgLikeMethod(SyntaxNodeAnalysisContext syntaxNodeContext, T

// if Arg is used with not allowed expression, find if it is used in ignored ones eg. var x = Arg.Any
// as variable might be used later on
if (enclosingExpression == null && FindIgnoredEnclosingExpression(argInvocationExpression) == null)
if (enclosingExpression == null)
{
var diagnostic = Diagnostic.Create(
DiagnosticDescriptorsProvider.NonSubstitutableMemberArgumentMatcherUsage,
argInvocationExpression.GetLocation());
var maybeIgnoredEnclosingExpression = FindMaybeIgnoredEnclosingExpression(argInvocationExpression);

syntaxNodeContext.ReportDiagnostic(diagnostic);
return;
if (maybeIgnoredEnclosingExpression == null)
{
var diagnostic = Diagnostic.Create(
DiagnosticDescriptorsProvider.NonSubstitutableMemberArgumentMatcherUsage,
argInvocationExpression.GetLocation());

syntaxNodeContext.ReportDiagnostic(diagnostic);
return;
}
}

if (enclosingExpression == null)
{
return;
}

if (syntaxNodeContext.SemanticModel.GetOperation(enclosingExpression).IsEventAssignmentOperation())
{
return;
}

var enclosingExpressionSymbol = syntaxNodeContext.SemanticModel.GetSymbolInfo(enclosingExpression).Symbol;

if (enclosingExpressionSymbol == null)
Expand All @@ -103,7 +113,7 @@ private SyntaxNode FindAllowedEnclosingExpression(TInvocationExpressionSyntax in
return invocationExpression.GetAncestorNode(AllowedAncestorPaths);
}

private SyntaxNode FindIgnoredEnclosingExpression(TInvocationExpressionSyntax invocationExpressionSyntax)
private SyntaxNode FindMaybeIgnoredEnclosingExpression(TInvocationExpressionSyntax invocationExpressionSyntax)
{
return invocationExpressionSyntax.GetAncestorNode(IgnoredAncestorPaths);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@ internal static class IOperationExtensions
{
public static bool IsEventAssignmentOperation(this IOperation operation)
{
return operation is IAssignmentOperation assignmentOperation &&
assignmentOperation.Kind == OperationKind.EventAssignment;
return operation switch
{
IAssignmentOperation assignmentOperation => assignmentOperation.Kind == OperationKind.EventAssignment,
IEventAssignmentOperation _ => true,
_ => false
};
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ internal sealed class NonSubstitutableMemberArgumentMatcherAnalyzer : AbstractNo
(int)SyntaxKind.CTypeExpression,
(int)SyntaxKind.SimpleArgument,
(int)SyntaxKind.ArgumentList,
(int)SyntaxKind.InvocationExpression));
(int)SyntaxKind.InvocationExpression),
ImmutableArray.Create((int)SyntaxKind.AddHandlerStatement));

private static ImmutableArray<ImmutableArray<int>> IgnoredPaths { get; } = ImmutableArray.Create(
ImmutableArray.Create(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ public abstract class NonSubstitutableMemberArgumentMatcherDiagnosticVerifier :
[MemberData(nameof(CorrectlyUsedArgTestCasesWithoutCasts))]
public abstract Task ReportsNoDiagnosticsForSuppressedMember_WhenSuppressingNonVirtualMethod(string arg);

[Fact]
public abstract Task ReportsNoDiagnostics_WhenSubscribingToEvent();

public static IEnumerable<object[]> MisusedArgTestCases
{
get
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -700,5 +700,32 @@ public void Test()

await VerifyDiagnostic(source, ArgumentMatcherUsedWithoutSpecifyingCall);
}

public override async Task ReportsNoDiagnostics_WhenSubscribingToEvent()
{
var source = @"using NSubstitute;
using System;
namespace MyNamespace
{
public class Foo
{
public event Action SomeEvent;
public int Bar()
{
return 2;
}
}
public class FooTests
{
public void Test()
{
var substitute = NSubstitute.Substitute.For<Foo>();
Received.InOrder(() => substitute.SomeEvent += Arg.Any<Action>());
}
}
}";
await VerifyNoDiagnostic(source);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,7 @@ public interface INonSubstitutableMemberArgumentMatcherDiagnosticVerifier
Task ReportsNoDiagnostics_WhenUsedInProtectedInternalVirtualMember(string arg);

Task ReportsNoDiagnosticsForSuppressedMember_WhenSuppressingNonVirtualMethod(string arg);

Task ReportsNoDiagnostics_WhenSubscribingToEvent();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ public abstract class NonSubstitutableMemberArgumentMatcherDiagnosticVerifier :
[MemberData(nameof(CorrectlyUsedArgTestCasesWithoutCasts))]
public abstract Task ReportsNoDiagnosticsForSuppressedMember_WhenSuppressingNonVirtualMethod(string arg);

[Fact]
public abstract Task ReportsNoDiagnostics_WhenSubscribingToEvent();

// VisualBasic specific case
[Fact]
public abstract Task ReportsNoDiagnostic_WhenOverloadCannotBeInferred();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,34 @@ End Namespace
await VerifyDiagnostic(source, ArgumentMatcherUsedWithoutSpecifyingCall);
}

public override async Task ReportsNoDiagnostics_WhenSubscribingToEvent()
{
var source = @"Imports NSubstitute
Imports System
Namespace MyNamespace
Public Class Foo
Public Event SomeEvent As Action
Public Function Bar() As Integer
Return 2
End Function
End Class
Public Class FooTests
Public Sub Test()
Dim substitute = NSubstitute.Substitute.[For](Of Foo)()
NSubstitute.Received.InOrder(Function()
AddHandler substitute.SomeEvent, Arg.Any(Of Action)()
End Function)
End Sub
End Class
End Namespace
";
await VerifyNoDiagnostic(source);
}

public override async Task ReportsNoDiagnostic_WhenOverloadCannotBeInferred()
{
var source = $@"Imports System
Expand Down

0 comments on commit c09694d

Please sign in to comment.