Skip to content

Commit

Permalink
[GH-1] - first draft of SubstituteAnalyzer adjusted to dev branch cha…
Browse files Browse the repository at this point in the history
…nges
  • Loading branch information
tpodolak committed Jun 10, 2018
1 parent ff9582a commit dcbc78d
Show file tree
Hide file tree
Showing 13 changed files with 176 additions and 31 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using NSubstitute.Analyzers.CSharp.Extensions;
using NSubstitute.Analyzers.Shared.DiagnosticAnalyzers;

namespace NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers
{
public class SubstituteAnalysis : AbstractSubstituteAnalysis<InvocationExpressionSyntax>
{
protected override IList<SyntaxNode> GetInvocationArguments(InvocationExpressionSyntax invocationExpression)
{
return invocationExpression.ArgumentList.Arguments.Select<ArgumentSyntax, SyntaxNode>(syntax => syntax).ToList();
}

// TODO get rid of casts
protected override IList<SyntaxNode> GetParameterExpressionsFromArrayArgument(SyntaxNode syntaxNode)
{
return ((ArgumentSyntax)syntaxNode).Expression.GetParameterExpressionsFromArrayArgument()
.Select<ExpressionSyntax, SyntaxNode>(syntax => syntax).ToList();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@
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
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
internal class SubstituteAnalyzer : AbstractSubstituteAnalyzer<SyntaxKind, InvocationExpressionSyntax, ExpressionSyntax>
{
private readonly SubstituteAnalysis _substituteAnalysis = new SubstituteAnalysis();
private readonly SubstituteConstructorMatcher _substituteConstructorMatcher = new SubstituteConstructorMatcher();

public SubstituteAnalyzer()
: base(new DiagnosticDescriptorsProvider())
{
Expand All @@ -25,12 +29,18 @@ protected override IEnumerable<ExpressionSyntax> GetTypeOfLikeExpressions(IList<

protected override IEnumerable<ExpressionSyntax> GetArrayInitializerArguments(InvocationExpressionSyntax invocationExpressionSyntax)
{
throw new System.NotImplementedException();
return invocationExpressionSyntax.ArgumentList.Arguments.Skip(1).First().Expression
.GetParameterExpressionsFromArrayArgument();
}

protected override ConstructorContext CollectConstructorContext(SubstituteContext<InvocationExpressionSyntax> substituteContext, ITypeSymbol proxyTypeSymbol)
{
throw new System.NotImplementedException();
return _substituteAnalysis.CollectConstructorContext(substituteContext, proxyTypeSymbol);
}

protected override bool MatchesInvocation(Compilation semanticModelCompilation, IMethodSymbol ctor, IList<ITypeSymbol> constructorContextInvocationParameters)
{
return _substituteConstructorMatcher.MatchesInvocation(semanticModelCompilation, ctor, constructorContextInvocationParameters);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using NSubstitute.Analyzers.Shared.DiagnosticAnalyzers;

namespace NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers
{
public class SubstituteConstructorMatcher : AbstractSubstituteConstructorMatcher
{
protected override bool ClasifyConversion(Compilation compilation, ITypeSymbol source, ITypeSymbol destination)
{
var conversion = compilation.ClassifyConversion(source, destination);

return conversion.Exists && conversion.IsImplicit;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace NSubstitute.Analyzers.CSharp.Extensions
{
public static class ExpressionSyntaxExtensions
{
private static readonly IList<ExpressionSyntax> EmptyExpressionList = new ExpressionSyntax[0];

public static IList<ExpressionSyntax> GetParameterExpressionsFromArrayArgument(this ExpressionSyntax expression)
{
InitializerExpressionSyntax initializer = null;
switch (expression.Kind())
{
case SyntaxKind.ArrayCreationExpression:
initializer = ((ArrayCreationExpressionSyntax)expression).Initializer;
break;
case SyntaxKind.ImplicitArrayCreationExpression:
initializer = ((ImplicitArrayCreationExpressionSyntax)expression).Initializer;
break;
default:
return null;
}

if (initializer == null)
{
return EmptyExpressionList;
}

return initializer.Expressions.ToList();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System.Collections.Immutable;
using System.Linq;
using System.Reflection.Metadata;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
Expand All @@ -10,12 +9,7 @@

namespace NSubstitute.Analyzers.Shared.CodeFixProviders
{
#if CSHARP
[ExportCodeFixProvider(LanguageNames.CSharp)]
#elif VISUAL_BASIC
[ExportCodeFixProvider(LanguageNames.VisualBasic)]
#endif
public class AbstractConstructorArgumentsForInterfaceCodeFixProvider : CodeFixProvider
internal class AbstractConstructorArgumentsForInterfaceCodeFixProvider : CodeFixProvider
{
public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,10 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
#if CSHARP
using Microsoft.CodeAnalysis.CSharp.Syntax;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
#elif VISUAL_BASIC
using Microsoft.CodeAnalysis.VisualBasic.Syntax;
using static Microsoft.CodeAnalysis.VisualBasic.SyntaxFactory;
#endif

namespace NSubstitute.Analyzers.Shared.CodeFixProviders
{
#if CSHARP
[ExportCodeFixProvider(LanguageNames.CSharp)]
#elif VISUAL_BASIC
[ExportCodeFixProvider(LanguageNames.VisualBasic)]
#endif
public class AbstractForPartsOfUsedForUnsupportedTypeCodeFixProvider : CodeFixProvider
internal class AbstractForPartsOfUsedForUnsupportedTypeCodeFixProvider : CodeFixProvider
{
// no completed task in .net standard
private static Task _completedTask = Task.FromResult(1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
namespace NSubstitute.Analyzers.Shared.DiagnosticAnalyzers
{
// TODO remove duplication
public abstract class SubstituteAnalysis<TInvocationExpression>
public abstract class AbstractSubstituteAnalysis<TInvocationExpression>
where TInvocationExpression : SyntaxNode
{
public ConstructorContext CollectConstructorContext(SubstituteContext<TInvocationExpression> substituteContext, ITypeSymbol proxyTypeSymbol)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ public override void Initialize(AnalysisContext context)

protected abstract ConstructorContext CollectConstructorContext(SubstituteContext<TInvocationExpressionSyntax> substituteContext, ITypeSymbol proxyTypeSymbol);

protected abstract bool MatchesInvocation(Compilation semanticModelCompilation, IMethodSymbol ctor, IList<ITypeSymbol> constructorContextInvocationParameters);

private void AnalyzeInvocation(SyntaxNodeAnalysisContext syntaxNodeContext)
{
var invocationExpression = (TInvocationExpressionSyntax)syntaxNodeContext.Node;
Expand Down Expand Up @@ -277,7 +279,7 @@ private bool AnalyzeConstructorInvocation(SubstituteContext<TInvocationExpressio
}

if (constructorContext.PossibleConstructors.All(ctor =>
SubstituteConstructorMatcher.MatchesInvocation(
MatchesInvocation(
substituteContext.SyntaxNodeAnalysisContext.SemanticModel.Compilation, ctor, constructorContext.InvocationParameters) ==
false))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
namespace NSubstitute.Analyzers.Shared.DiagnosticAnalyzers
{
// TODO refactor
public static class SubstituteConstructorMatcher
public abstract class AbstractSubstituteConstructorMatcher
{
// even though conversion returns that key -> value is convertible it fails on the runtime when runninig through substitute creation
private static readonly Dictionary<SpecialType, SpecialType> WellKnownUnsupportedConversions =
protected virtual Dictionary<SpecialType, SpecialType> WellKnownUnsupportedConversions { get; } =
new Dictionary<SpecialType, SpecialType>
{
[SpecialType.System_Int16] = SpecialType.System_Decimal,
Expand All @@ -19,7 +19,7 @@ public static class SubstituteConstructorMatcher
[SpecialType.System_UInt64] = SpecialType.System_Decimal
};

private static readonly Dictionary<SpecialType, HashSet<SpecialType>> WellKnownSupportedConversions =
protected virtual Dictionary<SpecialType, HashSet<SpecialType>> WellKnownSupportedConversions { get; } =
new Dictionary<SpecialType, HashSet<SpecialType>>
{
[SpecialType.System_Char] = new HashSet<SpecialType>
Expand All @@ -34,7 +34,7 @@ public static class SubstituteConstructorMatcher
}
};

public static bool MatchesInvocation(Compilation compilation, IMethodSymbol methodSymbol, IList<ITypeSymbol> invocationParameters)
public bool MatchesInvocation(Compilation compilation, IMethodSymbol methodSymbol, IList<ITypeSymbol> invocationParameters)
{
if (methodSymbol.Parameters.Length != invocationParameters.Count)
{
Expand All @@ -46,7 +46,9 @@ public static bool MatchesInvocation(Compilation compilation, IMethodSymbol meth
.Count() == methodSymbol.Parameters.Length;
}

private static bool IsConvertible(Compilation compilation, ITypeSymbol source, ITypeSymbol destination)
protected abstract bool ClasifyConversion(Compilation compilation, ITypeSymbol source, ITypeSymbol destination);

private bool IsConvertible(Compilation compilation, ITypeSymbol source, ITypeSymbol destination)
{
if (source == null || source.Equals(destination))
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.VisualBasic.Syntax;
using NSubstitute.Analyzers.Shared.DiagnosticAnalyzers;
using NSubstitute.Analyzers.VisualBasic.Extensions;

namespace NSubstitute.Analyzers.VisualBasic.DiagnosticAnalyzers
{
public class SubstituteAnalysis : AbstractSubstituteAnalysis<InvocationExpressionSyntax>
{
protected override IList<SyntaxNode> GetInvocationArguments(InvocationExpressionSyntax invocationExpression)
{
return invocationExpression.ArgumentList.Arguments.Select<ArgumentSyntax, SyntaxNode>(syntax => syntax).ToList();
}

// TODO get rid of casts
protected override IList<SyntaxNode> GetParameterExpressionsFromArrayArgument(SyntaxNode syntaxNode)
{
return ((ArgumentSyntax)syntaxNode).GetExpression().GetParameterExpressionsFromArrayArgument()
.Select<ExpressionSyntax, SyntaxNode>(syntax => syntax).ToList();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@
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 SubstituteAnalyzer : AbstractSubstituteAnalyzer<SyntaxKind, InvocationExpressionSyntax, ExpressionSyntax>
{
private readonly SubstituteAnalysis _substituteAnalysis = new SubstituteAnalysis();
private readonly SubstituteConstructorMatcher _substituteConstructorMatcher = new SubstituteConstructorMatcher();

public SubstituteAnalyzer()
: base(new DiagnosticDescriptorsProvider())
{
Expand All @@ -25,12 +29,18 @@ protected override IEnumerable<ExpressionSyntax> GetTypeOfLikeExpressions(IList<

protected override IEnumerable<ExpressionSyntax> GetArrayInitializerArguments(InvocationExpressionSyntax invocationExpressionSyntax)
{
throw new System.NotImplementedException();
return invocationExpressionSyntax.ArgumentList.Arguments.Skip(1).First().GetExpression()
.GetParameterExpressionsFromArrayArgument();
}

protected override ConstructorContext CollectConstructorContext(SubstituteContext<InvocationExpressionSyntax> substituteContext, ITypeSymbol proxyTypeSymbol)
{
throw new System.NotImplementedException();
return _substituteAnalysis.CollectConstructorContext(substituteContext, proxyTypeSymbol);
}

protected override bool MatchesInvocation(Compilation semanticModelCompilation, IMethodSymbol ctor, IList<ITypeSymbol> constructorContextInvocationParameters)
{
return _substituteConstructorMatcher.MatchesInvocation(semanticModelCompilation, ctor, constructorContextInvocationParameters);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.VisualBasic;
using NSubstitute.Analyzers.Shared.DiagnosticAnalyzers;

namespace NSubstitute.Analyzers.VisualBasic.DiagnosticAnalyzers
{
public class SubstituteConstructorMatcher : AbstractSubstituteConstructorMatcher
{
protected override bool ClasifyConversion(Compilation compilation, ITypeSymbol source, ITypeSymbol destination)
{
var conversion = compilation.ClassifyConversion(source, destination);

return conversion.Exists && conversion.IsNarrowing;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis.VisualBasic;
using Microsoft.CodeAnalysis.VisualBasic.Syntax;

namespace NSubstitute.Analyzers.VisualBasic.Extensions
{
public static class ExpressionSyntaxExtensions
{
public static IList<ExpressionSyntax> GetParameterExpressionsFromArrayArgument(this ExpressionSyntax expression)
{
switch (expression.Kind())
{
case SyntaxKind.ArrayCreationExpression:
var initializer = ((ArrayCreationExpressionSyntax)expression).Initializer;
return initializer.Initializers.ToList();
case SyntaxKind.CollectionInitializer:
return ((CollectionInitializerSyntax)expression).Initializers.ToList();
default:
return null;
}
}
}
}

0 comments on commit dcbc78d

Please sign in to comment.