Skip to content

Commit

Permalink
[GH-30] - Visual Basic implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
tpodolak committed Aug 24, 2018
1 parent 03badcf commit 5d484b5
Show file tree
Hide file tree
Showing 9 changed files with 946 additions and 76 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
using System.Collections.Generic;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using NSubstitute.Analyzers.Shared;
using NSubstitute.Analyzers.Shared.DiagnosticAnalyzers;

namespace NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers
Expand All @@ -13,5 +16,56 @@ public override CallInfoContext<InvocationExpressionSyntax, ElementAccessExpress

return new CallInfoContext<InvocationExpressionSyntax, ElementAccessExpressionSyntax>(visitor.ArgAtInvocations, visitor.ArgInvocations, visitor.DirectIndexerAccesses);
}

private class CallInfoVisitor : CSharpSyntaxWalker
{
private readonly SemanticModel _semanticModel;

public List<InvocationExpressionSyntax> ArgAtInvocations { get; }

public List<InvocationExpressionSyntax> ArgInvocations { get; }

public List<ElementAccessExpressionSyntax> DirectIndexerAccesses { get; }

public CallInfoVisitor(SemanticModel semanticModel)
{
_semanticModel = semanticModel;
DirectIndexerAccesses = new List<ElementAccessExpressionSyntax>();
ArgAtInvocations = new List<InvocationExpressionSyntax>();
ArgInvocations = new List<InvocationExpressionSyntax>();
}

public override void VisitInvocationExpression(InvocationExpressionSyntax node)
{
var symbolInfo = ModelExtensions.GetSymbolInfo(_semanticModel, node);

if (symbolInfo.Symbol != null &&
symbolInfo.Symbol.ContainingType.ToString().Equals(MetadataNames.NSubstituteCoreFullTypeName))
{
if (symbolInfo.Symbol.Name == MetadataNames.CallInfoArgAtMethod)
{
ArgAtInvocations.Add(node);
}

if (symbolInfo.Symbol.Name == MetadataNames.CallInfoArgMethod)
{
ArgInvocations.Add(node);
}
}

base.VisitInvocationExpression(node);
}

public override void VisitElementAccessExpression(ElementAccessExpressionSyntax node)
{
var symbolInfo = ModelExtensions.GetSymbolInfo(_semanticModel, node).Symbol ?? ModelExtensions.GetSymbolInfo(_semanticModel, node.Expression).Symbol;
if (symbolInfo != null && symbolInfo.ContainingType.ToString().Equals(MetadataNames.NSubstituteCoreFullTypeName))
{
DirectIndexerAccesses.Add(node);
}

base.VisitElementAccessExpression(node);
}
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
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;

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

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

protected override SyntaxNode GetParentMethodCall(InvocationExpressionSyntax invocationExpressionSyntax)
{
return invocationExpressionSyntax.Expression.DescendantNodes().First();
}

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

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

protected override SyntaxNode GetSafeCastTypeExpression(InvocationExpressionSyntax indexerExpressionSyntax)
{
if (indexerExpressionSyntax.Parent is TryCastExpressionSyntax directCastExpressionSyntax)
{
return directCastExpressionSyntax.Type;
}

return null;
}

protected override SyntaxNode GetUnsafeCastTypeExpression(InvocationExpressionSyntax indexerExpressionSyntax)
{
if (indexerExpressionSyntax.Parent is DirectCastExpressionSyntax directCastExpressionSyntax)
{
return directCastExpressionSyntax.Type;
}

if (indexerExpressionSyntax.Parent is CTypeExpressionSyntax cTypeExpressionSyntax)
{
return cTypeExpressionSyntax.Type;
}

return null;
}

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

return null;
}

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

protected override int? GetArgAtPosition(SyntaxNodeAnalysisContext syntaxNodeAnalysisContext, InvocationExpressionSyntax invocationExpressionSyntax)
{
var argAtPosition = syntaxNodeAnalysisContext.SemanticModel.GetConstantValue(invocationExpressionSyntax.ArgumentList.Arguments.First().GetExpression());

return (int?)(argAtPosition.HasValue ? argAtPosition.Value : null);
}

protected override int? GetIndexerPosition(SyntaxNodeAnalysisContext syntaxNodeAnalysisContext, InvocationExpressionSyntax indexerExpressionSyntax)
{
var indexerPosition = syntaxNodeAnalysisContext.SemanticModel.GetConstantValue(indexerExpressionSyntax.ArgumentList.Arguments.First().GetExpression());

return (int?)(indexerPosition.HasValue ? indexerPosition.Value : null);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using System.Collections.Generic;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.VisualBasic;
using Microsoft.CodeAnalysis.VisualBasic.Syntax;
using NSubstitute.Analyzers.Shared;
using NSubstitute.Analyzers.Shared.DiagnosticAnalyzers;

namespace NSubstitute.Analyzers.VisualBasic.DiagnosticAnalyzers
{
internal class CallInfoCallFinder : AbstractCallInfoFinder<InvocationExpressionSyntax, InvocationExpressionSyntax>
{
public override CallInfoContext<InvocationExpressionSyntax, InvocationExpressionSyntax> GetCallInfoContext(SemanticModel semanticModel, SyntaxNode syntaxNode)
{
var visitor = new CallInfoVisitor(semanticModel);
visitor.Visit(syntaxNode);

return new CallInfoContext<InvocationExpressionSyntax, InvocationExpressionSyntax>(visitor.ArgAtInvocations, visitor.ArgInvocations, visitor.DirectIndexerAccesses);
}

private class CallInfoVisitor : VisualBasicSyntaxWalker
{
private readonly SemanticModel _semanticModel;

public List<InvocationExpressionSyntax> ArgAtInvocations { get; }

public List<InvocationExpressionSyntax> ArgInvocations { get; }

public List<InvocationExpressionSyntax> DirectIndexerAccesses { get; }

public CallInfoVisitor(SemanticModel semanticModel)
{
_semanticModel = semanticModel;
DirectIndexerAccesses = new List<InvocationExpressionSyntax>();
ArgAtInvocations = new List<InvocationExpressionSyntax>();
ArgInvocations = new List<InvocationExpressionSyntax>();
}

public override void VisitInvocationExpression(InvocationExpressionSyntax node)
{
var symbol = _semanticModel.GetSymbolInfo(node).Symbol;

if (symbol != null && symbol.ContainingType.ToString().Equals(MetadataNames.NSubstituteCoreFullTypeName))
{
if (symbol.Name == MetadataNames.CallInfoArgAtMethod)
{
ArgAtInvocations.Add(node);
}

if (symbol.Name == MetadataNames.CallInfoArgMethod)
{
ArgInvocations.Add(node);
}
}

var expressionSymbol = _semanticModel.GetSymbolInfo(node.Expression).Symbol;
if (symbol == null && expressionSymbol != null && expressionSymbol.ContainingType.ToString().Equals(MetadataNames.NSubstituteCoreFullTypeName))
{
DirectIndexerAccesses.Add(node);
}

base.VisitInvocationExpression(node);
}

public override void VisitMemberAccessExpression(MemberAccessExpressionSyntax node)
{
base.VisitMemberAccessExpression(node);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,44 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Diagnostics;
using NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers;
using NSubstitute.Analyzers.Tests.Shared.DiagnosticAnalyzers;

namespace NSubstitute.Analyzers.Tests.CSharp.DiagnosticAnalyzerTests.CallInfoAnalyzerTests
{
public class CallInfoDiagnosticVerifier : CSharpDiagnosticVerifier
public abstract class CallInfoDiagnosticVerifier : CSharpDiagnosticVerifier, ICallInfoDiagnosticVerifier
{
public abstract Task ReportsDiagnostic_WhenAccessingArgumentOutOfBounds(string argAccess, int expectedLine, int expectedColumn);

public abstract Task ReportsNoDiagnostic_WhenAccessingArgumentWithinBounds(string argAccess);

public abstract Task ReportsDiagnostic_WhenConvertingTypeToUnsupportedType(string argAccess, int expectedLine, int expectedColumn);

public abstract Task ReportsNoDiagnostic_WhenConvertingTypeToSupportedType(string argAccess);

public abstract Task ReportsNoDiagnostic_WhenCastingElementsFromArgTypes(string argAccess);

public abstract Task ReportsNoDiagnostic_WhenAssigningValueToNotRefNorOutArgumentViaIndirectCall(string argAccess);

public abstract Task ReportsDiagnostic_WhenAccessingArgumentByTypeNotInInvocation();

public abstract Task ReportsNoDiagnostic_WhenAccessingArgumentByTypeInInInvocation();

public abstract Task ReportsDiagnostic_WhenAccessingArgumentByTypeMultipleTimesInInvocation();

public abstract Task ReportsNoDiagnostic_WhenAccessingArgumentByTypeMultipleDifferentTypesInInvocation();

public abstract Task ReportsDiagnostic_WhenAssigningValueToNotOutNorRefArgument();

public abstract Task ReportsNoDiagnostic_WhenAssigningValueToRefArgument();

public abstract Task ReportsNoDiagnostic_WhenAssigningValueToOutArgument();

public abstract Task ReportsDiagnostic_WhenAssigningValueToOutOfBoundsArgument();

public abstract Task ReportsDiagnostic_WhenAssigningWrongTypeToArgument();

public abstract Task ReportsNoDiagnostic_WhenAssigningProperTypeToArgument();

protected override DiagnosticAnalyzer GetDiagnosticAnalyzer()
{
return new CallInfoAnalyzer();
Expand Down
Loading

0 comments on commit 5d484b5

Please sign in to comment.