Skip to content

Commit

Permalink
[GH-62] - conflicting ref/out arguments - visual basic implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
tpodolak committed Mar 10, 2019
1 parent f3a29f8 commit a8275d2
Show file tree
Hide file tree
Showing 8 changed files with 1,033 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,22 +27,7 @@ protected override IEnumerable<ExpressionSyntax> GetArgumentExpressions(Invocati

protected override SyntaxNode GetSubstituteCall(SyntaxNodeAnalysisContext syntaxNodeContext, IMethodSymbol methodSymbol, InvocationExpressionSyntax invocationExpressionSyntax)
{
if (methodSymbol.IsExtensionMethod)
{
switch (methodSymbol.MethodKind)
{
case MethodKind.ReducedExtension:
return invocationExpressionSyntax.Expression.DescendantNodes().First();
case MethodKind.Ordinary:
return invocationExpressionSyntax.ArgumentList.Arguments.First().Expression;
default:
return null;
}
}

var parentInvocation = invocationExpressionSyntax.GetParentInvocationExpression();

return parentInvocation;
return invocationExpressionSyntax.GetParentInvocationExpression();
}

protected override AbstractCallInfoFinder<InvocationExpressionSyntax, ElementAccessExpressionSyntax> GetCallInfoFinder()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.VisualBasic;
using Microsoft.CodeAnalysis.VisualBasic.Syntax;
using NSubstitute.Analyzers.Shared.DiagnosticAnalyzers;
using NSubstitute.Analyzers.VisualBasic.Extensions;

namespace NSubstitute.Analyzers.VisualBasic.DiagnosticAnalyzers
{
[DiagnosticAnalyzer(LanguageNames.VisualBasic)]
internal class ConflictingArgumentAssignmentsAnalyzer : AbstractConflictingArgumentAssignmentsAnalyzer<SyntaxKind, InvocationExpressionSyntax, ExpressionSyntax, InvocationExpressionSyntax>
{
public ConflictingArgumentAssignmentsAnalyzer()
: base(new DiagnosticDescriptorsProvider())
{
}

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

protected override IEnumerable<ExpressionSyntax> GetArgumentExpressions(InvocationExpressionSyntax invocationExpressionSyntax)
{
return invocationExpressionSyntax.ArgumentList.Arguments.Select(arg => arg.GetExpression());
}

protected override SyntaxNode GetSubstituteCall(SyntaxNodeAnalysisContext syntaxNodeContext, IMethodSymbol methodSymbol, InvocationExpressionSyntax invocationExpressionSyntax)
{
return invocationExpressionSyntax.GetParentInvocationExpression();
}

protected override AbstractCallInfoFinder<InvocationExpressionSyntax, InvocationExpressionSyntax> GetCallInfoFinder()
{
return new CallInfoCallFinder();
}

protected override int? GetIndexerPosition(SyntaxNodeAnalysisContext syntaxNodeAnalysisContext, InvocationExpressionSyntax indexerExpressionSyntax)
{
var position = syntaxNodeAnalysisContext.SemanticModel.GetConstantValue(indexerExpressionSyntax.ArgumentList.Arguments.First().GetExpression());
return (int?)(position.HasValue ? position.Value : null);
}

protected override ISymbol GetIndexerSymbol(SyntaxNodeAnalysisContext syntaxNodeAnalysisContext, InvocationExpressionSyntax indexerExpressionSyntax)
{
return ModelExtensions.GetSymbolInfo(syntaxNodeAnalysisContext.SemanticModel, indexerExpressionSyntax).Symbol ??
ModelExtensions.GetSymbolInfo(syntaxNodeAnalysisContext.SemanticModel, indexerExpressionSyntax.Expression).Symbol;
}

protected override SyntaxNode GetAssignmentExpression(InvocationExpressionSyntax indexerExpressionSyntax)
{
if (indexerExpressionSyntax.Parent is AssignmentStatementSyntax assignmentExpressionSyntax)
{
return assignmentExpressionSyntax.Right;
}

return null;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
using System.Threading.Tasks;
using NSubstitute.Analyzers.Tests.Shared.Extensibility;

namespace NSubstitute.Analyzers.Tests.VisualBasic.DiagnosticAnalyzersTests.ConflictingArgumentAssignmentsAnalyzerTests
{
[CombinatoryData("AndDoes")]
public class AndDoesAsOrdinaryMethodTests : ConflictingArgumentAssignmentsDiagnosticVerifier
{
public override async Task ReportsDiagnostic_When_AndDoesMethod_SetsSameArgument_AsPreviousSetupMethod(string method, string call, string previousCallArgAccess, string andDoesArgAccess)
{
var source = $@"using System;
using NSubstitute;
namespace MyNamespace
{{
public interface Foo
{{
int Bar(int x);
int Barr {{ get; }}
int this[int x] {{ get; }}
}}
public class FooTests
{{
public void Test()
{{
var substitute = NSubstitute.Substitute.For<Foo>();
{call}.Returns(1).{method}(callInfo =>
{{
{previousCallArgAccess}
}}).AndDoes(callInfo =>
{{
{andDoesArgAccess}
}});
}}
}}
}}";

await VerifyDiagnostic(source, Descriptor);
}

public override async Task ReportsNoDiagnostics_When_AndDoesMethod_SetsDifferentArgument_AsPreviousSetupMethod(string method, string call, string andDoesArgAccess)
{
var source = $@"using System;
using NSubstitute;
namespace MyNamespace
{{
public interface Foo
{{
int Bar(int x);
int Barr {{ get; }}
int this[int x] {{ get; }}
}}
public class FooTests
{{
public void Test()
{{
var substitute = NSubstitute.Substitute.For<Foo>();
{call}.Returns(1).{method}(callInfo =>
{{
callInfo[0] = 1;
}}).AndDoes(callInfo =>
{{
{andDoesArgAccess}
}});
}}
}}
}}";

await VerifyNoDiagnostic(source);
}

public override async Task ReportsNoDiagnostics_When_AndDoesMethod_AccessSameArguments_AsPreviousSetupMethod(string method, string call, string argAccess)
{
var source = $@"using System;
using NSubstitute;
namespace MyNamespace
{{
public interface Foo
{{
int Bar(int x);
int Barr {{ get; }}
int this[int x] {{ get; }}
}}
public class FooTests
{{
public void Test()
{{
var substitute = NSubstitute.Substitute.For<Foo>();
{call}.Returns(1).{method}(callInfo =>
{{
{argAccess}
}}).AndDoes(callInfo =>
{{
{argAccess}
}});
}}
}}
}}";

await VerifyNoDiagnostic(source);
}

public override async Task ReportsNoDiagnostics_When_AndDoesMethod_SetSameArguments_AsPreviousSetupMethod_SetsIndirectly(string method)
{
var source = $@"using System;
using NSubstitute;
namespace MyNamespace
{{
public interface Foo
{{
int Bar(int x);
}}
public class FooTests
{{
public void Test()
{{
var substitute = NSubstitute.Substitute.For<Foo>();
substitute.Bar(Arg.Any<int>()).Returns(1).{method}(callInfo =>
{{
callInfo.Args()[0] = 1;
callInfo.ArgTypes()[0] = typeof(int);
((byte[])callInfo[0])[0] = 1;
}}).AndDoes(callInfo =>
{{
callInfo[0] = 1;
}});
}}
}}
}}";

await VerifyNoDiagnostic(source);
}

public override async Task ReportsNoDiagnostic_When_AndDoesMethod_SetArgument_AndPreviousMethod_IsNotUsedWithCallInfo(string method, string call, string andDoesArgAccess)
{
var source = $@"using System;
using NSubstitute;
namespace MyNamespace
{{
public interface Foo
{{
int Bar(int x);
int Barr {{ get; }}
int this[int x] {{ get; }}
}}
public class FooTests
{{
public void Test()
{{
var substitute = NSubstitute.Substitute.For<Foo>();
{call}.Returns(1).{method}(_ => {{}}).AndDoes(callInfo =>
{{
{andDoesArgAccess}
}});
}}
}}
}}";

await VerifyNoDiagnostic(source);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using NSubstitute.Analyzers.Shared;
using NSubstitute.Analyzers.Tests.Shared.DiagnosticAnalyzers;
using NSubstitute.Analyzers.Tests.Shared.Extensibility;
using NSubstitute.Analyzers.VisualBasic;
using NSubstitute.Analyzers.VisualBasic.DiagnosticAnalyzers;
using Xunit;

namespace NSubstitute.Analyzers.Tests.VisualBasic.DiagnosticAnalyzersTests.ConflictingArgumentAssignmentsAnalyzerTests
{
public abstract class ConflictingArgumentAssignmentsDiagnosticVerifier : VisualBasicDiagnosticVerifier, IConflictingArgumentAssignmentsDiagnosticVerifier
{
public DiagnosticDescriptor Descriptor { get; } = DiagnosticDescriptors<DiagnosticDescriptorsProvider>.ConflictingAssignmentsToOutRefArgument;

[CombinatoryTheory]
[InlineData("substitute.Bar(Arg.Any<int>())", "callInfo[1] = 1;", "[|callInfo[1]|] = 1;")]
[InlineData("substitute.Barr", "callInfo[1] = 1;", "[|callInfo[1]|] = 1;")]
[InlineData("substitute[Arg.Any<int>()]", "callInfo[1] = 1;", "[|callInfo[1]|] = 1;")]
public abstract Task ReportsDiagnostic_When_AndDoesMethod_SetsSameArgument_AsPreviousSetupMethod(string method, string call, string previousCallArgAccess, string andDoesArgAccess);

[CombinatoryTheory]
[InlineData("substitute.Bar(Arg.Any<int>())", "callInfo[1] = 1;")]
[InlineData("substitute.Barr", "callInfo[1] = 1;")]
[InlineData("substitute[Arg.Any<int>()]", "callInfo[1] = 1;")]
public abstract Task ReportsNoDiagnostics_When_AndDoesMethod_SetsDifferentArgument_AsPreviousSetupMethod(string method, string call, string andDoesArgAccess);

[CombinatoryTheory]
[InlineData("substitute.Bar(Arg.Any<int>())", "var x = callInfo[1];")]
[InlineData("substitute.Barr", "var x = callInfo[1];")]
[InlineData("substitute[Arg.Any<int>()]", "var x = callInfo[1];")]
public abstract Task ReportsNoDiagnostics_When_AndDoesMethod_AccessSameArguments_AsPreviousSetupMethod(string method, string call, string argAccess);

[CombinatoryTheory]
[InlineData]
public abstract Task ReportsNoDiagnostics_When_AndDoesMethod_SetSameArguments_AsPreviousSetupMethod_SetsIndirectly(string method);

[CombinatoryTheory]
[InlineData("substitute.Bar(Arg.Any<int>())", "callInfo[1] = 1;")]
[InlineData("substitute.Barr", "callInfo[1] = 1;")]
[InlineData("substitute[Arg.Any<int>()]", "callInfo[1] = 1;")]
public abstract Task ReportsNoDiagnostic_When_AndDoesMethod_SetArgument_AndPreviousMethod_IsNotUsedWithCallInfo(string method, string call, string andDoesArgAccess);

protected override DiagnosticAnalyzer GetDiagnosticAnalyzer()
{
return new ConflictingArgumentAssignmentsAnalyzer();
}
}
}
Loading

0 comments on commit a8275d2

Please sign in to comment.