Skip to content

Commit

Permalink
GH-108 - finding non-virtual calls withint Received.InOrder block
Browse files Browse the repository at this point in the history
  • Loading branch information
tpodolak committed Aug 5, 2020
1 parent b87e925 commit 56e04db
Show file tree
Hide file tree
Showing 508 changed files with 1,120 additions and 20 deletions.
Empty file modified .gitattributes
100644 → 100755
Empty file.
Empty file modified .gitignore
100644 → 100755
Empty file.
Empty file modified Acknowledgements.md
100644 → 100755
Empty file.
Empty file modified Analyzers.ruleset
100644 → 100755
Empty file.
Empty file modified Directory.Build.props
100644 → 100755
Empty file.
Empty file modified LICENSE.md
100644 → 100755
Empty file.
Empty file modified NSubstitute.Analyzers.All.sln
100644 → 100755
Empty file.
Empty file modified NSubstitute.Analyzers.sln
100644 → 100755
Empty file.
Empty file modified README.md
100644 → 100755
Empty file.
Empty file modified ReleaseNotes.md
100644 → 100755
Empty file.
Empty file modified StyleCop.json
100644 → 100755
Empty file.
Empty file modified appveyor.yml
100644 → 100755
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file modified benchmarks/NSubstitute.Analyzers.Benchmarks/Program.cs
100644 → 100755
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file modified build/build.cake
100644 → 100755
Empty file.
Empty file modified build/build.config
100644 → 100755
Empty file.
Empty file modified build/build.ps1
100644 → 100755
Empty file.
Empty file modified build/build.sh
100644 → 100755
Empty file.
Empty file modified build/parameters.cake
100644 → 100755
Empty file.
Empty file modified build/paths.cake
100644 → 100755
Empty file.
Empty file modified build/releasenotes.cake
100644 → 100755
Empty file.
Empty file modified build/table-of-contents.cake
100644 → 100755
Empty file.
Empty file modified build/version.cake
100644 → 100755
Empty file.
Empty file modified documentation/Compatibility.md
100644 → 100755
Empty file.
Empty file modified documentation/Configuration.md
100644 → 100755
Empty file.
Empty file modified documentation/README.md
100644 → 100755
Empty file.
Empty file modified documentation/rules/NS1000.md
100644 → 100755
Empty file.
Empty file modified documentation/rules/NS1001.md
100644 → 100755
Empty file.
Empty file modified documentation/rules/NS1002.md
100644 → 100755
Empty file.
Empty file modified documentation/rules/NS1003.md
100644 → 100755
Empty file.
Empty file modified documentation/rules/NS1004.md
100644 → 100755
Empty file.
Empty file modified documentation/rules/NS2000.md
100644 → 100755
Empty file.
Empty file modified documentation/rules/NS2001.md
100644 → 100755
Empty file.
Empty file modified documentation/rules/NS2002.md
100644 → 100755
Empty file.
Empty file modified documentation/rules/NS2003.md
100644 → 100755
Empty file.
Empty file modified documentation/rules/NS2004.md
100644 → 100755
Empty file.
Empty file modified documentation/rules/NS2005.md
100644 → 100755
Empty file.
Empty file modified documentation/rules/NS2006.md
100644 → 100755
Empty file.
Empty file modified documentation/rules/NS2007.md
100644 → 100755
Empty file.
Empty file modified documentation/rules/NS3000.md
100644 → 100755
Empty file.
Empty file modified documentation/rules/NS3001.md
100644 → 100755
Empty file.
Empty file modified documentation/rules/NS3002.md
100644 → 100755
Empty file.
Empty file modified documentation/rules/NS3003.md
100644 → 100755
Empty file.
Empty file modified documentation/rules/NS3004.md
100644 → 100755
Empty file.
Empty file modified documentation/rules/NS3005.md
100644 → 100755
Empty file.
Empty file modified documentation/rules/NS3006.md
100644 → 100755
Empty file.
Empty file modified documentation/rules/NS4000.md
100644 → 100755
Empty file.
Empty file modified documentation/rules/NS5000.md
100644 → 100755
Empty file.
Empty file modified documentation/rules/NS5001.md
100644 → 100755
Empty file.
Empty file modified documentation/rules/NS5002.md
100644 → 100755
Empty file.
Empty file modified documentation/rules/README.md
100644 → 100755
Empty file.
Empty file modified global.json
100644 → 100755
Empty file.
Empty file modified images/csharp-example.png
100644 → 100755
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file modified images/nsubstitute-analyser.ai
100644 → 100755
Empty file.
Empty file modified images/nsubstitute-analyser.png
100644 → 100755
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file modified images/nsubstitute-analyser@0,25x.png
100644 → 100755
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file modified images/nsubstitute-analyser@0,5x.png
100644 → 100755
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file modified images/nsubstitute-analyser@1x.png
100644 → 100755
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file modified images/vb-example.png
100644 → 100755
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file modified nsubstitute.snk
100644 → 100755
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
namespace NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
internal class NonSubstitutableMemberReceivedInOrderAnalyzer : AbstractNonSubstitutableMemberReceivedInOrderAnalyzer<SyntaxKind, InvocationExpressionSyntax, MemberAccessExpressionSyntax>
internal class NonSubstitutableMemberReceivedInOrderAnalyzer : AbstractNonSubstitutableMemberReceivedInOrderAnalyzer<SyntaxKind, InvocationExpressionSyntax, MemberAccessExpressionSyntax, BlockSyntax>
{
private static ImmutableArray<ImmutableArray<int>> IgnoredPaths { get; } = ImmutableArray.Create(
ImmutableArray.Create(
Expand All @@ -17,14 +17,6 @@ internal class NonSubstitutableMemberReceivedInOrderAnalyzer : AbstractNonSubsti
(int)SyntaxKind.CastExpression,
(int)SyntaxKind.Argument),
ImmutableArray.Create(
(int)SyntaxKind.EqualsValueClause,
(int)SyntaxKind.VariableDeclarator),
ImmutableArray.Create(
(int)SyntaxKind.CastExpression,
(int)SyntaxKind.EqualsValueClause,
(int)SyntaxKind.VariableDeclarator),
ImmutableArray.Create(
(int)SyntaxKind.AsExpression,
(int)SyntaxKind.EqualsValueClause,
(int)SyntaxKind.VariableDeclarator));

Expand All @@ -36,5 +28,10 @@ public NonSubstitutableMemberReceivedInOrderAnalyzer()
protected override SyntaxKind InvocationExpressionKind { get; } = SyntaxKind.InvocationExpression;

protected override ImmutableArray<ImmutableArray<int>> IgnoredAncestorPaths { get; } = IgnoredPaths;

protected override ISymbol GetDeclarationSymbol(SemanticModel semanticModel, SyntaxNode node)
{
return semanticModel.GetDeclaredSymbol(node);
}
}
}
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file modified src/NSubstitute.Analyzers.CSharp/Extensions/SyntaxExtensions.cs
100644 → 100755
Empty file.
Empty file.
Empty file modified src/NSubstitute.Analyzers.CSharp/Properties/AssemblyInfo.cs
100644 → 100755
Empty file.
Empty file.
Empty file.
Empty file.
4 changes: 2 additions & 2 deletions src/NSubstitute.Analyzers.CSharp/Resources.Designer.cs
100644 → 100755

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/NSubstitute.Analyzers.CSharp/Resources.resx
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@
<value>Member {0} can not be intercepted. Only interface members and virtual, overriding, and abstract members can be intercepted.</value>
<comment>The format-able message the diagnostic displays.</comment>
</data>
<data name="NonVirtualReceivedInOrderSetupSpecificationTitleMessageFormat" xml:space="preserve">
<data name="NonVirtualReceivedInOrderSetupSpecificationMessageFormat" xml:space="preserve">
<value>Member {0} can not be intercepted. Only interface members and virtual, overriding, and abstract members can be intercepted.</value>
<comment>The format-able message the diagnostic displays.</comment>
</data>
Expand Down
Empty file modified src/NSubstitute.Analyzers.CSharp/tools/install.ps1
100644 → 100755
Empty file.
Empty file modified src/NSubstitute.Analyzers.CSharp/tools/uninstall.ps1
100644 → 100755
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
34 changes: 32 additions & 2 deletions ...yzers.Shared/DiagnosticAnalyzers/AbstractNonSubstitutableMemberReceivedInOrderAnalyzer.cs
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@

namespace NSubstitute.Analyzers.Shared.DiagnosticAnalyzers
{
internal abstract class AbstractNonSubstitutableMemberReceivedInOrderAnalyzer<TSyntaxKind, TInvocationExpressionSyntax, TMemberAccessExpressionSyntax> : AbstractDiagnosticAnalyzer
internal abstract class AbstractNonSubstitutableMemberReceivedInOrderAnalyzer<TSyntaxKind, TInvocationExpressionSyntax, TMemberAccessExpressionSyntax, TBlockStatementSyntax> : AbstractDiagnosticAnalyzer
where TSyntaxKind : struct
where TInvocationExpressionSyntax : SyntaxNode
where TMemberAccessExpressionSyntax : SyntaxNode
where TBlockStatementSyntax : SyntaxNode
{
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; }

Expand Down Expand Up @@ -38,6 +39,8 @@ protected sealed override void InitializeAnalyzer(AnalysisContext context)
context.RegisterSyntaxNodeAction(_analyzeInvocationAction, InvocationExpressionKind);
}

protected abstract ISymbol GetDeclarationSymbol(SemanticModel semanticModel, SyntaxNode node);

private void AnalyzeInvocation(SyntaxNodeAnalysisContext syntaxNodeContext)
{
var invocationExpression = (TInvocationExpressionSyntax)syntaxNodeContext.Node;
Expand All @@ -56,7 +59,7 @@ private void AnalyzeInvocation(SyntaxNodeAnalysisContext syntaxNodeContext)
foreach (var syntaxNode in _substitutionNodeFinder.FindForReceivedInOrderExpression(
syntaxNodeContext,
invocationExpression,
(IMethodSymbol)methodSymbolInfo.Symbol).Where(node => FindIgnoredEnclosingExpression(node) == null))
(IMethodSymbol)methodSymbolInfo.Symbol).Where(node => ShouldAnalyzeNode(syntaxNodeContext.SemanticModel, node)))
{
var symbolInfo = syntaxNodeContext.SemanticModel.GetSymbolInfo(syntaxNode);

Expand Down Expand Up @@ -89,6 +92,33 @@ private void AnalyzeInvocation(SyntaxNodeAnalysisContext syntaxNodeContext)
}
}

private bool ShouldAnalyzeNode(SemanticModel semanticModel, SyntaxNode syntaxNode)
{
var maybeIgnoredExpression = FindIgnoredEnclosingExpression(syntaxNode);
if (maybeIgnoredExpression == null)
{
return true;
}

var symbol = GetDeclarationSymbol(semanticModel, maybeIgnoredExpression);

if (symbol == null)
{
return false;
}

var blockStatementSyntax =
maybeIgnoredExpression.Ancestors().OfType<TBlockStatementSyntax>().FirstOrDefault();

if (blockStatementSyntax == null)
{
return false;
}

var dataFlowAnalysis = semanticModel.AnalyzeDataFlow(blockStatementSyntax);
return !dataFlowAnalysis.ReadInside.Contains(symbol);
}

private Location GetSubstitutionNodeActualLocation(SyntaxNode syntaxNode, ISymbol symbol)
{
return syntaxNode.GetSubstitutionNodeActualLocation<TMemberAccessExpressionSyntax>(symbol);
Expand Down
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file modified src/NSubstitute.Analyzers.Shared/DiagnosticCategories.cs
100644 → 100755
Empty file.
Empty file modified src/NSubstitute.Analyzers.Shared/DiagnosticDescriptors.cs
100644 → 100755
Empty file.
Empty file modified src/NSubstitute.Analyzers.Shared/DiagnosticIdentifiers.cs
100644 → 100755
Empty file.
Empty file modified src/NSubstitute.Analyzers.Shared/DisplayNameAttribute.cs
100644 → 100755
Empty file.
Empty file.
Empty file.
Empty file.
Empty file modified src/NSubstitute.Analyzers.Shared/Extensions/EnumExtensions.cs
100644 → 100755
Empty file.
Empty file.
Empty file.
Empty file.
Empty file modified src/NSubstitute.Analyzers.Shared/Extensions/ObjectExtensions.cs
100644 → 100755
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file modified src/NSubstitute.Analyzers.Shared/GlobalSuppressions.cs
100644 → 100755
Empty file.
Empty file.
Empty file modified src/NSubstitute.Analyzers.Shared/MetadataNames.cs
100644 → 100755
Empty file.
Empty file.
Empty file modified src/NSubstitute.Analyzers.Shared/Properties/AssemblyInfo.cs
100644 → 100755
Empty file.
Empty file modified src/NSubstitute.Analyzers.Shared/Resources.Designer.cs
100644 → 100755
Empty file.
Empty file modified src/NSubstitute.Analyzers.Shared/Resources.resx
100644 → 100755
Empty file.
Empty file modified src/NSubstitute.Analyzers.Shared/Settings/AnalyzersSettings.cs
100644 → 100755
Empty file.
Empty file modified src/NSubstitute.Analyzers.Shared/Settings/Suppression.cs
100644 → 100755
Empty file.
Empty file.
Empty file modified src/NSubstitute.Analyzers.Shared/SharedResourceManager.cs
100644 → 100755
Empty file.
Empty file modified src/NSubstitute.Analyzers.Shared/TinyJson/Json.cs
100644 → 100755
Empty file.
Empty file modified src/NSubstitute.Analyzers.Shared/TinyJson/JsonBuilder.cs
100644 → 100755
Empty file.
Empty file modified src/NSubstitute.Analyzers.Shared/TinyJson/JsonMapper.cs
100644 → 100755
Empty file.
Empty file modified src/NSubstitute.Analyzers.Shared/TinyJson/JsonParser.cs
100644 → 100755
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using System.Collections.Immutable;
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 NonSubstitutableMemberReceivedInOrderAnalyzer : AbstractNonSubstitutableMemberReceivedInOrderAnalyzer<SyntaxKind, InvocationExpressionSyntax, MemberAccessExpressionSyntax, LambdaExpressionSyntax>
{
private static ImmutableArray<ImmutableArray<int>> IgnoredPaths { get; } = ImmutableArray.Create(
ImmutableArray.Create(
(int)SyntaxKind.SimpleArgument,
(int)SyntaxKind.ArgumentList,
(int)SyntaxKind.InvocationExpression),
ImmutableArray.Create(
(int)SyntaxKind.TryCastExpression,
(int)SyntaxKind.SimpleArgument,
(int)SyntaxKind.ArgumentList,
(int)SyntaxKind.InvocationExpression),
ImmutableArray.Create(
(int)SyntaxKind.PredefinedCastExpression,
(int)SyntaxKind.SimpleArgument,
(int)SyntaxKind.ArgumentList,
(int)SyntaxKind.InvocationExpression),
ImmutableArray.Create(
(int)SyntaxKind.EqualsValue,
(int)SyntaxKind.VariableDeclarator),
ImmutableArray.Create(
(int)SyntaxKind.TryCastExpression,
(int)SyntaxKind.EqualsValue,
(int)SyntaxKind.VariableDeclarator),
ImmutableArray.Create(
(int)SyntaxKind.DirectCastExpression,
(int)SyntaxKind.EqualsValue,
(int)SyntaxKind.VariableDeclarator),
ImmutableArray.Create(
(int)SyntaxKind.CTypeExpression,
(int)SyntaxKind.EqualsValue,
(int)SyntaxKind.VariableDeclarator));

public NonSubstitutableMemberReceivedInOrderAnalyzer()
: base(SubstitutionNodeFinder.Instance, NSubstitute.Analyzers.VisualBasic.DiagnosticDescriptorsProvider.Instance)
{
}

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

protected override ImmutableArray<ImmutableArray<int>> IgnoredAncestorPaths { get; } = IgnoredPaths;

protected override ISymbol GetDeclarationSymbol(SemanticModel semanticModel, SyntaxNode node)
{
var symbol = semanticModel.GetDeclaredSymbol(node);

if (symbol != null)
{
return symbol;
}

if (!(node is VariableDeclaratorSyntax variableDeclaratorSyntax))
{
return null;
}

var modifiedIdentifierSyntax = variableDeclaratorSyntax.Names.FirstOrDefault();

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

return semanticModel.GetDeclaredSymbol(modifiedIdentifierSyntax);
}
}
}
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
4 changes: 2 additions & 2 deletions src/NSubstitute.Analyzers.VisualBasic/Resources.Designer.cs
100644 → 100755

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/NSubstitute.Analyzers.VisualBasic/Resources.resx
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@
<value>Member {0} can not be intercepted. Only interface members and overrideable, overriding, and must override members can be intercepted.</value>
<comment>The format-able message the diagnostic displays.</comment>
</data>
<data name="NonVirtualReceivedInOrderSetupSpecificationTitleMessageFormat" xml:space="preserve">
<data name="NonVirtualReceivedInOrderSetupSpecificationMessageFormat" xml:space="preserve">
<value>Member {0} can not be intercepted. Only interface members and overrideable, overriding, and must override members can be intercepted.</value>
<comment>The format-able message the diagnostic displays.</comment>
</data>
Expand Down
Empty file modified src/NSubstitute.Analyzers.VisualBasic/tools/install.ps1
100644 → 100755
Empty file.
Empty file modified src/NSubstitute.Analyzers.VisualBasic/tools/uninstall.ps1
100644 → 100755
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Loading

0 comments on commit 56e04db

Please sign in to comment.