Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
…yzers into GH-87-NS1002-higlights-wrong-member
  • Loading branch information
tpodolak committed Apr 7, 2019
2 parents 6e3b3cf + 2d39f9d commit 7351fd9
Show file tree
Hide file tree
Showing 27 changed files with 2,940 additions and 327 deletions.
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, MemberAccessExpressionSyntax>
{
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

0 comments on commit 7351fd9

Please sign in to comment.