diff --git a/benchmarks/NSubstitute.Analyzers.Benchmarks/CSharp/CSharpDiagnosticAnalyzersBenchmarks.cs b/benchmarks/NSubstitute.Analyzers.Benchmarks/CSharp/CSharpDiagnosticAnalyzersBenchmarks.cs index 214cc78a..512de1d5 100644 --- a/benchmarks/NSubstitute.Analyzers.Benchmarks/CSharp/CSharpDiagnosticAnalyzersBenchmarks.cs +++ b/benchmarks/NSubstitute.Analyzers.Benchmarks/CSharp/CSharpDiagnosticAnalyzersBenchmarks.cs @@ -23,7 +23,7 @@ public class CSharpDiagnosticAnalyzersBenchmarks : AbstractDiagnosticAnalyzersBe protected override AnalyzerBenchmark UnusedReceivedAnalyzerBenchmark { get; } - protected override AnalyzerBenchmark ArgumentMatcherAnalyzerBenchmark { get; } + protected override AnalyzerBenchmark ArgumentMatcherAnalyzerBenchmark { get; set; } protected override AbstractSolutionLoader SolutionLoader { get; } @@ -45,7 +45,12 @@ public CSharpDiagnosticAnalyzersBenchmarks() ReEntrantSetupAnalyzerBenchmark = AnalyzerBenchmark.CreateBenchmark(Solution, new ReEntrantSetupAnalyzer()); SubstituteAnalyzerBenchmark = AnalyzerBenchmark.CreateBenchmark(Solution, new SubstituteAnalyzer()); UnusedReceivedAnalyzerBenchmark = AnalyzerBenchmark.CreateBenchmark(Solution, new UnusedReceivedAnalyzer()); - ArgumentMatcherAnalyzerBenchmark = AnalyzerBenchmark.CreateBenchmark(Solution, new ArgumentMatcherAnalyzer()); + ArgumentMatcherAnalyzerBenchmark = CreateArgumentMatcherAnalyzerBenchmark(); + } + + protected override AnalyzerBenchmark CreateArgumentMatcherAnalyzerBenchmark() + { + return AnalyzerBenchmark.CreateBenchmark(Solution, new ArgumentMatcherAnalyzer()); } } } \ No newline at end of file diff --git a/benchmarks/NSubstitute.Analyzers.Benchmarks/Shared/AbstractDiagnosticAnalyzersBenchmarks.cs b/benchmarks/NSubstitute.Analyzers.Benchmarks/Shared/AbstractDiagnosticAnalyzersBenchmarks.cs index 4ffd5cc1..4e0b004b 100644 --- a/benchmarks/NSubstitute.Analyzers.Benchmarks/Shared/AbstractDiagnosticAnalyzersBenchmarks.cs +++ b/benchmarks/NSubstitute.Analyzers.Benchmarks/Shared/AbstractDiagnosticAnalyzersBenchmarks.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Reflection; using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Engines; using Microsoft.CodeAnalysis; namespace NSubstitute.Analyzers.Benchmarks.Shared @@ -30,7 +31,7 @@ public abstract class AbstractDiagnosticAnalyzersBenchmarks protected abstract AnalyzerBenchmark UnusedReceivedAnalyzerBenchmark { get; } - protected abstract AnalyzerBenchmark ArgumentMatcherAnalyzerBenchmark { get; } + protected abstract AnalyzerBenchmark ArgumentMatcherAnalyzerBenchmark { get; set; } protected abstract AbstractSolutionLoader SolutionLoader { get; } @@ -99,6 +100,16 @@ public void ArgumentMatcherAnalyzer() ArgumentMatcherAnalyzerBenchmark.Run(); } + [IterationCleanup(Target = nameof(ArgumentMatcherAnalyzer))] + public void CleanUp() + { + // recreating argument matcher analyzer as it is a solution-wide stateful analyzer + // and there is no way to clear the state + ArgumentMatcherAnalyzerBenchmark = CreateArgumentMatcherAnalyzerBenchmark(); + } + + protected abstract AnalyzerBenchmark CreateArgumentMatcherAnalyzerBenchmark(); + private string GetBenchmarkSourceProjectPath() { var rootDirectory = FindRootDirectory(); diff --git a/benchmarks/NSubstitute.Analyzers.Benchmarks/Shared/AnalyzerBenchmark.cs b/benchmarks/NSubstitute.Analyzers.Benchmarks/Shared/AnalyzerBenchmark.cs index 1382927a..2552c6e9 100644 --- a/benchmarks/NSubstitute.Analyzers.Benchmarks/Shared/AnalyzerBenchmark.cs +++ b/benchmarks/NSubstitute.Analyzers.Benchmarks/Shared/AnalyzerBenchmark.cs @@ -17,6 +17,7 @@ private AnalyzerBenchmark( DiagnosticAnalyzer analyzer, IReadOnlyList> syntaxNodeActions, IReadOnlyList> compilationStartActions, + IReadOnlyList> compilationEndActions, IReadOnlyList> compilationActions, IReadOnlyList> semanticModelActions, IReadOnlyList> symbolActions, @@ -30,6 +31,7 @@ private AnalyzerBenchmark( Analyzer = analyzer; SyntaxNodeActions = syntaxNodeActions; CompilationStartActions = compilationStartActions; + CompilationEndActions = compilationEndActions; CompilationActions = compilationActions; SemanticModelActions = semanticModelActions; SymbolActions = symbolActions; @@ -52,6 +54,8 @@ public interface IContextAndAction public IReadOnlyList> CompilationStartActions { get; } + public IReadOnlyList> CompilationEndActions { get; } + public IReadOnlyList> CompilationActions { get; } public IReadOnlyList> SemanticModelActions { get; } @@ -80,6 +84,7 @@ public static async Task CreateBenchmarkAsync(Solution soluti analyzer, benchmarkAnalyzer.SyntaxNodeActions, benchmarkAnalyzer.CompilationStartActions, + benchmarkAnalyzer.CompilationEndActions, benchmarkAnalyzer.CompilationActions, benchmarkAnalyzer.SemanticModelActions, benchmarkAnalyzer.SymbolActions, @@ -147,6 +152,11 @@ public void Run() { contextAndAction.Run(); } + + foreach (var contextAndAction in CompilationEndActions) + { + contextAndAction.Run(); + } } public static async Task>> GetDiagnosticsAsync(Solution solution, DiagnosticAnalyzer analyzer) @@ -192,6 +202,8 @@ private class BenchmarkAnalyzer : DiagnosticAnalyzer internal List> CompilationStartActions { get; } = new List>(); + internal List> CompilationEndActions { get; } = new List>(); + internal List> CompilationActions { get; } = new List>(); internal List> SemanticModelActions { get; } = new List>(); @@ -254,7 +266,11 @@ public override void RegisterSyntaxNodeAction(Action action) { _context.RegisterCompilationStartAction( - x => _analyzer.CompilationStartActions.Add(new ContextAndAction(x, action))); + x => + { + _analyzer.CompilationStartActions.Add(new ContextAndAction(x, action)); + action(new BenchmarkCompilationStartAnalysisContext(_analyzer, x)); + }); } public override void RegisterCompilationAction(Action action) @@ -313,6 +329,59 @@ public override void RegisterOperationBlockStartAction(Action _analyzer.OperationBlockStartActions.Add(new ContextAndAction(x, action))); } } + + private class BenchmarkCompilationStartAnalysisContext : CompilationStartAnalysisContext + { + private readonly BenchmarkAnalyzer _analyzer; + private readonly CompilationStartAnalysisContext _inner; + +#pragma warning disable RS1012 // Start action has no registered actions + public BenchmarkCompilationStartAnalysisContext(BenchmarkAnalyzer analyzer, CompilationStartAnalysisContext inner) + : base(inner.Compilation, inner.Options, inner.CancellationToken) + { + _analyzer = analyzer; + _inner = inner; + } +#pragma warning restore RS1012 + + public override void RegisterCompilationEndAction(Action action) + { + _inner.RegisterCompilationEndAction(x => _analyzer.CompilationEndActions.Add(new ContextAndAction(x, action))); + } + + public override void RegisterSemanticModelAction(Action action) + { + _inner.RegisterSemanticModelAction(x => _analyzer.SemanticModelActions.Add(new ContextAndAction(x, action))); + } + + public override void RegisterSymbolAction(Action action, ImmutableArray symbolKinds) + { + _inner.RegisterSymbolAction( + x => _analyzer.SymbolActions.Add(new ContextAndAction(x, action)), symbolKinds); + } + + public override void RegisterCodeBlockStartAction(Action> action) + { + _inner.RegisterCodeBlockStartAction(x => + _analyzer.CodeBlockStartActions.Add( + new ContextAndAction>(x, action))); + } + + public override void RegisterCodeBlockAction(Action action) + { + _inner.RegisterCodeBlockAction(x => _analyzer.CodeBlockActions.Add(new ContextAndAction(x, action))); + } + + public override void RegisterSyntaxTreeAction(Action action) + { + _inner.RegisterSyntaxTreeAction(x => _analyzer.SyntaxTreeActions.Add(new ContextAndAction(x, action))); + } + + public override void RegisterSyntaxNodeAction(Action action, ImmutableArray syntaxKinds) + { + _inner.RegisterSyntaxNodeAction(x => _analyzer.SyntaxNodeActions.Add(new ContextAndAction(x, action)), syntaxKinds); + } + } } } } diff --git a/benchmarks/NSubstitute.Analyzers.Benchmarks/VisualBasic/VisualBasicDiagnosticAnalyzersBenchmarks.cs b/benchmarks/NSubstitute.Analyzers.Benchmarks/VisualBasic/VisualBasicDiagnosticAnalyzersBenchmarks.cs index 8d6ec171..af9a661f 100644 --- a/benchmarks/NSubstitute.Analyzers.Benchmarks/VisualBasic/VisualBasicDiagnosticAnalyzersBenchmarks.cs +++ b/benchmarks/NSubstitute.Analyzers.Benchmarks/VisualBasic/VisualBasicDiagnosticAnalyzersBenchmarks.cs @@ -23,7 +23,7 @@ public class VisualBasicDiagnosticAnalyzersBenchmarks : AbstractDiagnosticAnalyz protected override AnalyzerBenchmark UnusedReceivedAnalyzerBenchmark { get; } - protected override AnalyzerBenchmark ArgumentMatcherAnalyzerBenchmark { get; } + protected override AnalyzerBenchmark ArgumentMatcherAnalyzerBenchmark { get; set; } protected override AbstractSolutionLoader SolutionLoader { get; } @@ -45,7 +45,12 @@ public VisualBasicDiagnosticAnalyzersBenchmarks() ReEntrantSetupAnalyzerBenchmark = AnalyzerBenchmark.CreateBenchmark(Solution, new ReEntrantSetupAnalyzer()); SubstituteAnalyzerBenchmark = AnalyzerBenchmark.CreateBenchmark(Solution, new SubstituteAnalyzer()); UnusedReceivedAnalyzerBenchmark = AnalyzerBenchmark.CreateBenchmark(Solution, new UnusedReceivedAnalyzer()); - ArgumentMatcherAnalyzerBenchmark = AnalyzerBenchmark.CreateBenchmark(Solution, new ArgumentMatcherAnalyzer()); + ArgumentMatcherAnalyzerBenchmark = CreateArgumentMatcherAnalyzerBenchmark(); + } + + protected override AnalyzerBenchmark CreateArgumentMatcherAnalyzerBenchmark() + { + return AnalyzerBenchmark.CreateBenchmark(Solution, new ArgumentMatcherAnalyzer()); } } } \ No newline at end of file