Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gh 76 - analyzing callInfo usages for When..Do like methods #86

Merged
merged 5 commits into from
Apr 6, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,54 +13,17 @@ namespace NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers
internal class CallInfoAnalyzer : AbstractCallInfoAnalyzer<SyntaxKind, InvocationExpressionSyntax, ExpressionSyntax, ElementAccessExpressionSyntax>
{
public CallInfoAnalyzer()
: base(new DiagnosticDescriptorsProvider())
: base(new DiagnosticDescriptorsProvider(), new CallInfoCallFinder(), new SubstitutionNodeFinder())
{
}

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

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();

if (parentInvocation == null)
{
return null;
}

var symbol = syntaxNodeContext.SemanticModel.GetSymbolInfo(parentInvocation);

if (symbol.Symbol is IMethodSymbol mSymbol && mSymbol.ReducedFrom == null)
{
return parentInvocation.ArgumentList.Arguments.First().Expression;
}

return parentInvocation.Expression.DescendantNodes().First();
}

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

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

protected override SyntaxNode GetCastTypeExpression(ElementAccessExpressionSyntax indexerExpressionSyntax)
{
switch (indexerExpressionSyntax.Parent)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@

namespace NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers
{
internal class CallInfoCallFinder : AbstractCallInfoFinder<InvocationExpressionSyntax, ElementAccessExpressionSyntax>
internal class CallInfoCallFinder : ICallInfoFinder<InvocationExpressionSyntax, ElementAccessExpressionSyntax>
{
public override CallInfoContext<InvocationExpressionSyntax, ElementAccessExpressionSyntax> GetCallInfoContext(SemanticModel semanticModel, SyntaxNode syntaxNode)
public CallInfoContext<InvocationExpressionSyntax, ElementAccessExpressionSyntax> GetCallInfoContext(SemanticModel semanticModel, SyntaxNode syntaxNode)
{
var visitor = new CallInfoVisitor(semanticModel);
visitor.Visit(syntaxNode);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers
internal class ConflictingArgumentAssignmentsAnalyzer : AbstractConflictingArgumentAssignmentsAnalyzer<SyntaxKind, InvocationExpressionSyntax, ExpressionSyntax, ElementAccessExpressionSyntax>
{
public ConflictingArgumentAssignmentsAnalyzer()
: base(new DiagnosticDescriptorsProvider())
: base(new DiagnosticDescriptorsProvider(), new CallInfoCallFinder())
{
}

Expand All @@ -30,11 +30,6 @@ protected override SyntaxNode GetSubstituteCall(SyntaxNodeAnalysisContext syntax
return invocationExpressionSyntax.GetParentInvocationExpression();
}

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

protected override int? GetIndexerPosition(SyntaxNodeAnalysisContext syntaxNodeAnalysisContext, ElementAccessExpressionSyntax indexerExpressionSyntax)
{
var position = syntaxNodeAnalysisContext.SemanticModel.GetConstantValue(indexerExpressionSyntax.ArgumentList.Arguments.First().Expression);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
Expand All @@ -12,62 +13,10 @@ namespace NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers
internal class NonSubstitutableMemberWhenAnalyzer : AbstractNonSubstitutableMemberWhenAnalyzer<SyntaxKind, InvocationExpressionSyntax>
{
public NonSubstitutableMemberWhenAnalyzer()
: base(new DiagnosticDescriptorsProvider())
: base(new DiagnosticDescriptorsProvider(), new SubstitutionNodeFinder())
{
}

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

protected override IEnumerable<SyntaxNode> GetExpressionsForAnalysys(SyntaxNodeAnalysisContext syntaxNodeAnalysisContext, IMethodSymbol methodSymbol, InvocationExpressionSyntax invocationExpressionSyntax)
{
var argumentListArguments = invocationExpressionSyntax.ArgumentList.Arguments;
var argumentSyntax = methodSymbol.MethodKind == MethodKind.ReducedExtension ? argumentListArguments.First() : argumentListArguments.Skip(1).First();
return GetExpressionsForAnalysys(syntaxNodeAnalysisContext, argumentSyntax.Expression);
}

private IEnumerable<SyntaxNode> GetExpressionsForAnalysys(SyntaxNodeAnalysisContext syntaxNodeContext, SyntaxNode argumentSyntax)
{
SyntaxNode body = null;
switch (argumentSyntax)
{
case SimpleLambdaExpressionSyntax simpleLambdaExpressionSyntax:
body = simpleLambdaExpressionSyntax.Body;
break;
case AnonymousFunctionExpressionSyntax anonymousFunctionExpressionSyntax:
body = anonymousFunctionExpressionSyntax.Body;
break;
case LocalFunctionStatementSyntax localFunctionStatementSyntax:
body = (SyntaxNode)localFunctionStatementSyntax.Body ?? localFunctionStatementSyntax.ExpressionBody;
break;
case MethodDeclarationSyntax methodDeclarationSyntax:
body = (SyntaxNode)methodDeclarationSyntax.Body ?? methodDeclarationSyntax.ExpressionBody;
break;
case IdentifierNameSyntax identifierNameSyntax:
var symbol = syntaxNodeContext.SemanticModel.GetSymbolInfo(identifierNameSyntax);
if (symbol.Symbol != null && symbol.Symbol.Locations.Any())
{
var location = symbol.Symbol.Locations.First();
var syntaxNode = location.SourceTree.GetRoot().FindNode(location.SourceSpan);

foreach (var expressionsForAnalysy in GetExpressionsForAnalysys(syntaxNodeContext, syntaxNode))
{
yield return expressionsForAnalysy;
}
}

break;
}

if (body == null)
{
yield break;
}

foreach (var invocationExpressionSyntax in body.DescendantNodes().Where(node => node.IsKind(SyntaxKind.SimpleMemberAccessExpression) ||
node.IsKind(SyntaxKind.ElementAccessExpression)))
{
yield return invocationExpressionSyntax;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,10 @@ namespace NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers
internal class ReEntrantSetupAnalyzer : AbstractReEntrantSetupAnalyzer<SyntaxKind, InvocationExpressionSyntax>
{
public ReEntrantSetupAnalyzer()
: base(new DiagnosticDescriptorsProvider())
: base(new DiagnosticDescriptorsProvider(), new ReEntrantCallFinder())
{
}

protected override AbstractReEntrantCallFinder GetReEntrantCallFinder()
{
return new ReEntrantCallFinder();
}

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

protected override IEnumerable<SyntaxNode> ExtractArguments(InvocationExpressionSyntax invocationExpressionSyntax)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using NSubstitute.Analyzers.CSharp.Extensions;
using NSubstitute.Analyzers.Shared.DiagnosticAnalyzers;

namespace NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers
{
/// <summary>
/// Finds nodes which are considered to be a part of substitution call. For instance substitute.Bar().Returns(1) will return substitute.Bar()
/// </summary>
internal class SubstitutionNodeFinder : AbstractSubstitutionNodeFinder<InvocationExpressionSyntax>
{
public override IEnumerable<SyntaxNode> FindForWhenExpression(SyntaxNodeAnalysisContext syntaxNodeContext, InvocationExpressionSyntax whenInvocationExpression, IMethodSymbol whenInvocationSymbol = null)
{
if (whenInvocationExpression == null)
{
return Enumerable.Empty<SyntaxNode>();
}

whenInvocationSymbol = whenInvocationSymbol ?? syntaxNodeContext.SemanticModel.GetSymbolInfo(whenInvocationExpression).Symbol as IMethodSymbol;

if (whenInvocationSymbol == null)
{
return Enumerable.Empty<SyntaxNode>();
}

var argumentExpression = whenInvocationSymbol.MethodKind == MethodKind.ReducedExtension
? whenInvocationExpression.ArgumentList.Arguments.First().Expression
: whenInvocationExpression.ArgumentList.Arguments.Skip(1).First().Expression;

return FindForWhenExpression(syntaxNodeContext, argumentExpression);
}

public override SyntaxNode FindForAndDoesExpression(SyntaxNodeAnalysisContext syntaxNodeContext, InvocationExpressionSyntax invocationExpression, IMethodSymbol invocationExpressionSymbol)
{
var parentInvocationExpression = invocationExpression?.GetParentInvocationExpression();
if (parentInvocationExpression == null)
{
return null;
}

var symbol = syntaxNodeContext.SemanticModel.GetSymbolInfo(parentInvocationExpression);

return symbol.Symbol is IMethodSymbol methodSymbol && methodSymbol.ReducedFrom == null
? parentInvocationExpression.ArgumentList.Arguments.First().Expression
: parentInvocationExpression.Expression.DescendantNodes().First();
}

public override SyntaxNode FindForStandardExpression(InvocationExpressionSyntax invocationExpressionSyntax, IMethodSymbol invocationExpressionSymbol)
{
switch (invocationExpressionSymbol.MethodKind)
{
case MethodKind.ReducedExtension:
return invocationExpressionSyntax.Expression.DescendantNodes().First();
case MethodKind.Ordinary:
return invocationExpressionSyntax.ArgumentList.Arguments.First().Expression;
default:
return null;
}
}

protected override InvocationExpressionSyntax GetParentInvocationExpression(InvocationExpressionSyntax invocationExpressionSyntax)
{
return invocationExpressionSyntax.GetParentInvocationExpression();
}

private IEnumerable<SyntaxNode> FindForWhenExpression(SyntaxNodeAnalysisContext syntaxNodeContext, SyntaxNode argumentSyntax)
{
SyntaxNode body = null;
switch (argumentSyntax)
{
case SimpleLambdaExpressionSyntax simpleLambdaExpressionSyntax:
body = simpleLambdaExpressionSyntax.Body;
break;
case AnonymousFunctionExpressionSyntax anonymousFunctionExpressionSyntax:
body = anonymousFunctionExpressionSyntax.Body;
break;
case LocalFunctionStatementSyntax localFunctionStatementSyntax:
body = (SyntaxNode)localFunctionStatementSyntax.Body ?? localFunctionStatementSyntax.ExpressionBody;
break;
case MethodDeclarationSyntax methodDeclarationSyntax:
body = (SyntaxNode)methodDeclarationSyntax.Body ?? methodDeclarationSyntax.ExpressionBody;
break;
case IdentifierNameSyntax identifierNameSyntax:
var symbol = syntaxNodeContext.SemanticModel.GetSymbolInfo(identifierNameSyntax);
if (symbol.Symbol != null && symbol.Symbol.Locations.Any())
{
var location = symbol.Symbol.Locations.First();
var syntaxNode = location.SourceTree.GetRoot().FindNode(location.SourceSpan);

foreach (var expressionForAnalysis in FindForWhenExpression(syntaxNodeContext, syntaxNode))
{
yield return expressionForAnalysis;
}
}

break;
}

if (body == null)
{
yield break;
}

foreach (var invocationExpressionSyntax in body.DescendantNodes().Where(node => node.IsKind(SyntaxKind.SimpleMemberAccessExpression) ||
node.IsKind(SyntaxKind.ElementAccessExpression)))
{
yield return invocationExpressionSyntax;
}
}
}
}
Loading