Skip to content

Commit

Permalink
[GH-61] - analyzing callInfo for AndDoes method
Browse files Browse the repository at this point in the history
  • Loading branch information
tpodolak committed Feb 7, 2019
1 parent 9565b1a commit 56df9bc
Show file tree
Hide file tree
Showing 9 changed files with 1,299 additions and 23 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Reflection;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
Expand All @@ -11,6 +14,11 @@ namespace NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers
[DiagnosticAnalyzer(LanguageNames.CSharp)]
internal class CallInfoAnalyzer : AbstractCallInfoAnalyzer<SyntaxKind, InvocationExpressionSyntax, ExpressionSyntax, ElementAccessExpressionSyntax>
{
private static ImmutableArray<Type> callHierarchy = ImmutableArray.Create(
typeof(MemberAccessExpressionSyntax),
typeof(InvocationExpressionSyntax),
typeof(MemberAccessExpressionSyntax));

public CallInfoAnalyzer()
: base(new DiagnosticDescriptorsProvider())
{
Expand All @@ -20,15 +28,38 @@ public CallInfoAnalyzer()

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

// TODO fix
using (var descendantNodesEnumerator = invocationExpressionSyntax.DescendantNodes().GetEnumerator())
{
var hierarchyEnumerator = callHierarchy.GetEnumerator();
while (hierarchyEnumerator.MoveNext() && descendantNodesEnumerator.MoveNext())
{
if (descendantNodesEnumerator.Current.GetType().GetTypeInfo().IsAssignableFrom(hierarchyEnumerator.Current.GetTypeInfo()) == false)
{
return null;
}
}

if (hierarchyEnumerator.MoveNext() == false && descendantNodesEnumerator.MoveNext())
{
return descendantNodesEnumerator.Current;
}
}

return null;
}

protected override IEnumerable<ExpressionSyntax> GetArgumentExpressions(InvocationExpressionSyntax invocationExpressionSyntax)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public override void VisitInvocationExpression(InvocationExpressionSyntax node)
{
var symbolInfo = _semanticModel.GetSymbolInfo(node);

if (symbolInfo.Symbol != null && symbolInfo.Symbol.ContainingType.ToString().Equals(MetadataNames.NSubstituteCoreFullTypeName))
if (symbolInfo.Symbol != null && symbolInfo.Symbol.ContainingType.ToString().Equals(MetadataNames.NSubstituteCallInfoFullTypeName))
{
switch (symbolInfo.Symbol.Name)
{
Expand All @@ -58,7 +58,7 @@ public override void VisitInvocationExpression(InvocationExpressionSyntax 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))
if (symbolInfo != null && symbolInfo.ContainingType.ToString().Equals(MetadataNames.NSubstituteCallInfoFullTypeName))
{
DirectIndexerAccesses.Add(node);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ protected AbstractCallInfoAnalyzer(IDiagnosticDescriptorsProvider diagnosticDesc
[MetadataNames.NSubstituteReturnsMethod] = MetadataNames.NSubstituteSubstituteExtensionsFullTypeName,
[MetadataNames.NSubstituteReturnsForAnyArgsMethod] = MetadataNames.NSubstituteSubstituteExtensionsFullTypeName,
[MetadataNames.NSubstituteThrowsMethod] = MetadataNames.NSubstituteExceptionExtensionsFullTypeName,
[MetadataNames.NSubstituteThrowsForAnyArgsMethod] = MetadataNames.NSubstituteExceptionExtensionsFullTypeName
[MetadataNames.NSubstituteThrowsForAnyArgsMethod] = MetadataNames.NSubstituteExceptionExtensionsFullTypeName,
[MetadataNames.NSubstituteAndDoesMethod] = MetadataNames.NSubstituteConfiguredCallFullTypeName
}.ToImmutableDictionary();

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(
Expand Down Expand Up @@ -265,10 +266,11 @@ private bool AnalyzeAssignment(SyntaxNodeAnalysisContext syntaxNodeContext, ILis

private bool SupportsCallInfo(SyntaxNodeAnalysisContext syntaxNodeContext, TInvocationExpressionSyntax syntax, IMethodSymbol methodSymbol)
{
// TODO Simplify
var allArguments = GetArgumentExpressions(syntax);
var argumentsForAnalysis = methodSymbol.MethodKind == MethodKind.ReducedExtension
? allArguments
: allArguments.Skip(1);
: methodSymbol.IsStatic ? allArguments.Skip(1) : allArguments;

if (MethodNames.TryGetValue(methodSymbol.Name, out var typeName) == false)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ public static bool IsCallInfoDelegate(this TypeInfo typeInfo, SemanticModel sema
var isCalledViaDelegate = typeSymbol != null &&
typeSymbol.TypeKind == TypeKind.Delegate &&
typeSymbol is INamedTypeSymbol namedTypeSymbol &&
namedTypeSymbol.ConstructedFrom.Equals(semanticModel.Compilation.GetTypeByMetadataName("System.Func`2")) &&
(namedTypeSymbol.ConstructedFrom.Equals(semanticModel.Compilation.GetTypeByMetadataName("System.Func`2")) ||
namedTypeSymbol.ConstructedFrom.Equals(semanticModel.Compilation.GetTypeByMetadataName("System.Action`1"))) &&
IsCallInfoParameter(namedTypeSymbol.TypeArguments.First());

return isCalledViaDelegate;
Expand All @@ -21,7 +22,7 @@ typeSymbol is INamedTypeSymbol namedTypeSymbol &&
private static bool IsCallInfoParameter(ITypeSymbol symbol)
{
return symbol.ContainingAssembly?.Name.Equals(MetadataNames.NSubstituteAssemblyName, StringComparison.OrdinalIgnoreCase) == true &&
symbol.ToString().Equals(MetadataNames.NSubstituteCoreFullTypeName, StringComparison.OrdinalIgnoreCase) == true;
symbol.ToString().Equals(MetadataNames.NSubstituteCallInfoFullTypeName, StringComparison.OrdinalIgnoreCase) == true;
}
}
}
4 changes: 3 additions & 1 deletion src/NSubstitute.Analyzers.Shared/MetadataNames.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ internal class MetadataNames
public const string NSubstituteSubstituteExtensionsFullTypeName = "NSubstitute.SubstituteExtensions";
public const string NSubstituteReturnsExtensionsFullTypeName = "NSubstitute.ReturnsExtensions.ReturnsExtensions";
public const string NSubstituteExceptionExtensionsFullTypeName = "NSubstitute.ExceptionExtensions.ExceptionExtensions";
public const string NSubstituteCoreFullTypeName = "NSubstitute.Core.CallInfo";
public const string NSubstituteCallInfoFullTypeName = "NSubstitute.Core.CallInfo";
public const string NSubstituteConfiguredCallFullTypeName = "NSubstitute.Core.ConfiguredCall";
public const string NSubstituteSubstituteFullTypeName = "NSubstitute.Substitute";
public const string NSubstituteFactoryFullTypeName = "NSubstitute.Core.ISubstituteFactory";
public const string NSubstituteReturnsMethod = "Returns";
public const string NSubstituteReturnsForAnyArgsMethod = "ReturnsForAnyArgs";
public const string NSubstituteThrowsMethod = "Throws";
public const string NSubstituteThrowsForAnyArgsMethod = "ThrowsForAnyArgs";
public const string NSubstituteAndDoesMethod = "AndDoes";
public const string NSubstituteReturnsNullMethod = "ReturnsNull";
public const string NSubstituteReturnsNullForAnyArgsMethod = "ReturnsNullForAnyArgs";
public const string NSubstituteDoMethod = "Do";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Reflection;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.VisualBasic;
Expand All @@ -11,6 +14,11 @@ namespace NSubstitute.Analyzers.VisualBasic.DiagnosticAnalyzers
[DiagnosticAnalyzer(LanguageNames.VisualBasic)]
internal class CallInfoAnalyzer : AbstractCallInfoAnalyzer<SyntaxKind, InvocationExpressionSyntax, ExpressionSyntax, InvocationExpressionSyntax>
{
private static ImmutableArray<Type> callHierarchy = ImmutableArray.Create(
typeof(MemberAccessExpressionSyntax),
typeof(InvocationExpressionSyntax),
typeof(MemberAccessExpressionSyntax));

public CallInfoAnalyzer()
: base(new DiagnosticDescriptorsProvider())
{
Expand All @@ -20,15 +28,38 @@ public CallInfoAnalyzer()

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

// TODO fix
using (var descendantNodesEnumerator = invocationExpressionSyntax.DescendantNodes().GetEnumerator())
{
case MethodKind.ReducedExtension:
return invocationExpressionSyntax.Expression.DescendantNodes().First();
case MethodKind.Ordinary:
return invocationExpressionSyntax.ArgumentList.Arguments.First().GetExpression();
default:
return null;
var hierarchyEnumerator = callHierarchy.GetEnumerator();
while (hierarchyEnumerator.MoveNext() && descendantNodesEnumerator.MoveNext())
{
if (descendantNodesEnumerator.Current.GetType().GetTypeInfo().IsAssignableFrom(hierarchyEnumerator.Current.GetTypeInfo()) == false)
{
return null;
}
}

if (hierarchyEnumerator.MoveNext() == false && descendantNodesEnumerator.MoveNext())
{
return descendantNodesEnumerator.Current;
}
}

return null;
}

protected override IEnumerable<ExpressionSyntax> GetArgumentExpressions(InvocationExpressionSyntax invocationExpressionSyntax)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public override void VisitInvocationExpression(InvocationExpressionSyntax node)
{
var symbol = _semanticModel.GetSymbolInfo(node).Symbol;

if (symbol != null && symbol.ContainingType.ToString().Equals(MetadataNames.NSubstituteCoreFullTypeName))
if (symbol != null && symbol.ContainingType.ToString().Equals(MetadataNames.NSubstituteCallInfoFullTypeName))
{
switch (symbol.Name)
{
Expand All @@ -59,7 +59,7 @@ public override void VisitInvocationExpression(InvocationExpressionSyntax node)
{
var expressionSymbol = _semanticModel.GetSymbolInfo(node.Expression).Symbol;

if (expressionSymbol != null && expressionSymbol.ContainingType.ToString().Equals(MetadataNames.NSubstituteCoreFullTypeName))
if (expressionSymbol != null && expressionSymbol.ContainingType.ToString().Equals(MetadataNames.NSubstituteCallInfoFullTypeName))
{
DirectIndexerAccesses.Add(node);
}
Expand Down
Loading

0 comments on commit 56df9bc

Please sign in to comment.