diff --git a/integrationtest/TargetProjects/NetCore/TargetProject/Constructs/Csharp4.cs b/integrationtest/TargetProjects/NetCore/TargetProject/Constructs/Csharp4.cs index 180e14b7d2..f13bb88b8e 100644 --- a/integrationtest/TargetProjects/NetCore/TargetProject/Constructs/Csharp4.cs +++ b/integrationtest/TargetProjects/NetCore/TargetProject/Constructs/Csharp4.cs @@ -8,4 +8,27 @@ public static void NamedParameters() Console.WriteLine(format: "{0:f}", arg0: 6.02214179e23); Console.WriteLine(arg0: 6.02214179e23, format: "{0:f}"); } + + // default parameters + public static void DefaultParameters() + { + ExampleDefaultParameterMethod("Hello"); + ExampleDefaultParameterMethod("Hello", "World"); + ExampleDefaultParameterMethod("Hello", "World", "Foo"); + ExampleDefaultParameterMethod("Hello", "World", "Foo", "Bar"); + } + + // default named parameters + public static void DefaultNamedParameters() + { + ExampleDefaultParameterMethod(required1: "Hello"); + ExampleDefaultParameterMethod(optional3: "Hello", required1: "World"); + ExampleDefaultParameterMethod(optional2: "Hello", required1: "World", optional1: "Foo"); + ExampleDefaultParameterMethod(optional3: "Hello", optional2: "World", required1: "Foo", optional1: "Bar"); + } + + private static void ExampleDefaultParameterMethod(string required1, string optional1 = "optional1", + string optional2 = "optional2", string optional3 = "optional3") + { + } } diff --git a/src/Stryker.Abstractions/Mutator.cs b/src/Stryker.Abstractions/Mutator.cs index 6b6f286be9..56238ebc00 100644 --- a/src/Stryker.Abstractions/Mutator.cs +++ b/src/Stryker.Abstractions/Mutator.cs @@ -41,5 +41,7 @@ public enum Mutator [MutatorDescription("String Method")] StringMethod, [MutatorDescription("Conditional operators")] - Conditional + Conditional, + [MutatorDescription("Default parameter")] + DefaultParameter } diff --git a/src/Stryker.Core/Stryker.Core.UnitTest/Compiling/CSharpRollbackProcessTests.cs b/src/Stryker.Core/Stryker.Core.UnitTest/Compiling/CSharpRollbackProcessTests.cs index b20835c7fc..05af17c958 100644 --- a/src/Stryker.Core/Stryker.Core.UnitTest/Compiling/CSharpRollbackProcessTests.cs +++ b/src/Stryker.Core/Stryker.Core.UnitTest/Compiling/CSharpRollbackProcessTests.cs @@ -112,7 +112,7 @@ public void SomeLinq() }; var codeInjection = new CodeInjection(); var placer = new MutantPlacer(codeInjection); - var mutator = new CsharpMutantOrchestrator(placer, options: options); + var mutator = new CSharpMutantOrchestrator(placer, options: options); var helpers = new List(); foreach (var (name, code) in codeInjection.MutantHelpers) { @@ -196,7 +196,7 @@ private void RefreshAccountNumber() }; var codeInjection = new CodeInjection(); var placer = new MutantPlacer(codeInjection); - var mutator = new CsharpMutantOrchestrator(placer, options: options); + var mutator = new CSharpMutantOrchestrator(placer, options: options); var helpers = new List(); foreach (var (name, code) in codeInjection.MutantHelpers) { diff --git a/src/Stryker.Core/Stryker.Core.UnitTest/Mutants/CsharpMutantOrchestratorTests.cs b/src/Stryker.Core/Stryker.Core.UnitTest/Mutants/CSharpMutantOrchestratorTests.cs similarity index 99% rename from src/Stryker.Core/Stryker.Core.UnitTest/Mutants/CsharpMutantOrchestratorTests.cs rename to src/Stryker.Core/Stryker.Core.UnitTest/Mutants/CSharpMutantOrchestratorTests.cs index af8c38040c..3970e3630f 100644 --- a/src/Stryker.Core/Stryker.Core.UnitTest/Mutants/CsharpMutantOrchestratorTests.cs +++ b/src/Stryker.Core/Stryker.Core.UnitTest/Mutants/CSharpMutantOrchestratorTests.cs @@ -13,7 +13,7 @@ namespace Stryker.Core.UnitTest.Mutants; [TestClass] -public class CsharpMutantOrchestratorTests : MutantOrchestratorTestsBase +public class CSharpMutantOrchestratorTests : MutantOrchestratorTestsBase { [TestMethod] public void ShouldNotMutateEmptyInterfaces() @@ -89,7 +89,7 @@ public void ShouldMutateBlockStatements() MutationLevel = MutationLevel.Complete, OptimizationMode = OptimizationModes.CoverageBasedTest, }; - Target = new CsharpMutantOrchestrator(new MutantPlacer(Injector), options: options); + Target = new CSharpMutantOrchestrator(new MutantPlacer(Injector), options: options); var source = @"private void Move() { @@ -939,7 +939,7 @@ public void ShouldNotMutateIfDisabledByComment() source = @"public void SomeMethod() { var x = 0; { - // Stryker disable all + // Stryker disable all x++; } x/=2; @@ -961,7 +961,7 @@ public void ShouldNotMutateIfDisabledByMultilineComment() { var source = @"public void SomeMethod() { var x = 0; - if (condition && other) /* Stryker disable once all */ { + if (condition && other) /* Stryker disable once all */ { x++; x*=2; } @@ -969,7 +969,7 @@ public void ShouldNotMutateIfDisabledByMultilineComment() }"; var expected = @"public void SomeMethod() {if(StrykerNamespace.MutantControl.IsActive(0)){}else{ var x = 0; - if ((StrykerNamespace.MutantControl.IsActive(2)?!(condition && other):(StrykerNamespace.MutantControl.IsActive(1)?condition || other:condition && other))) /* Stryker disable once all */ { + if ((StrykerNamespace.MutantControl.IsActive(2)?!(condition && other):(StrykerNamespace.MutantControl.IsActive(1)?condition || other:condition && other))) /* Stryker disable once all */ { x++; x*=2; } @@ -1008,7 +1008,7 @@ public void ShouldNotMutateIfDisabledByMultilineComment() @"if ((StrykerNamespace.MutantControl.IsActive(1)?!(cond):cond)) // Stryker disable once all x++;")] [DataRow("if (/* Stryker disable once all*/cond) x++;", "if (/* Stryker disable once all*/cond) if(StrykerNamespace.MutantControl.IsActive(2)){;}else{if(StrykerNamespace.MutantControl.IsActive(3)){x--;}else{x++;}}")] - + public void ShouldNotMutateDependingOnWhereMultilineCommentIs(string source, string expected) { // must call reset as MsTest reuse test instance on/datarow @@ -1933,9 +1933,9 @@ public void ShouldIncrementMutantCountUniquely() }; var firstOrchestrator = - new CsharpMutantOrchestrator(new MutantPlacer(Injector), options: strykerOptions); + new CSharpMutantOrchestrator(new MutantPlacer(Injector), options: strykerOptions); var secondOrchestrator = - new CsharpMutantOrchestrator(new MutantPlacer(Injector), options: strykerOptions); + new CSharpMutantOrchestrator(new MutantPlacer(Injector), options: strykerOptions); var node = SyntaxFactory.ParseExpression("1 == 1") as BinaryExpressionSyntax; var firstMutant = firstOrchestrator diff --git a/src/Stryker.Core/Stryker.Core.UnitTest/Mutants/MutantOrchestratorTestsBase.cs b/src/Stryker.Core/Stryker.Core.UnitTest/Mutants/MutantOrchestratorTestsBase.cs index 8918db842b..41b4f00aaa 100644 --- a/src/Stryker.Core/Stryker.Core.UnitTest/Mutants/MutantOrchestratorTestsBase.cs +++ b/src/Stryker.Core/Stryker.Core.UnitTest/Mutants/MutantOrchestratorTestsBase.cs @@ -14,10 +14,10 @@ namespace Stryker.Core.UnitTest.Mutants; /// public class MutantOrchestratorTestsBase : TestBase { - protected CsharpMutantOrchestrator Target; + protected CSharpMutantOrchestrator Target; protected CodeInjection Injector = new(); - public MutantOrchestratorTestsBase() => Target = new CsharpMutantOrchestrator(new MutantPlacer(Injector), options: new StrykerOptions + public MutantOrchestratorTestsBase() => Target = new CSharpMutantOrchestrator(new MutantPlacer(Injector), options: new StrykerOptions { MutationLevel = MutationLevel.Complete, OptimizationMode = OptimizationModes.CoverageBasedTest, diff --git a/src/Stryker.Core/Stryker.Core.UnitTest/Mutants/MutantPlacerTests.cs b/src/Stryker.Core/Stryker.Core.UnitTest/Mutants/MutantPlacerTests.cs index 4dc04ed92e..dc48e997be 100644 --- a/src/Stryker.Core/Stryker.Core.UnitTest/Mutants/MutantPlacerTests.cs +++ b/src/Stryker.Core/Stryker.Core.UnitTest/Mutants/MutantPlacerTests.cs @@ -212,7 +212,7 @@ public void ShouldRollBackFailedConstructor() var source = @"class Test { static TestClass()=> Value-='a';}"; - var orchestrator = new CsharpMutantOrchestrator(placer, options: new StrykerOptions + var orchestrator = new CSharpMutantOrchestrator(placer, options: new StrykerOptions { OptimizationMode = OptimizationModes.CoverageBasedTest, MutationLevel = MutationLevel.Complete diff --git a/src/Stryker.Core/Stryker.Core.UnitTest/Mutators/DefaultParameterMutatorTests.cs b/src/Stryker.Core/Stryker.Core.UnitTest/Mutators/DefaultParameterMutatorTests.cs new file mode 100644 index 0000000000..3d6363a31b --- /dev/null +++ b/src/Stryker.Core/Stryker.Core.UnitTest/Mutators/DefaultParameterMutatorTests.cs @@ -0,0 +1,155 @@ +using System.Collections.Generic; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Shouldly; +using Stryker.Abstractions; +using Stryker.Abstractions.Mutants; +using Stryker.Abstractions.Mutators; +using Stryker.Core.Mutants; +using Stryker.Core.Mutators; + +namespace Stryker.Core.UnitTest.Mutators; + +[TestClass] +public class DefaultParameterMutatorTests +{ + [TestMethod] + public void ShouldBeMutationLevelComplete() + { + var options = new StrykerOptions(); + var orchestratorMock = new Mock(); + var target = new DefaultParameterMutator(orchestratorMock.Object, options); + target.MutationLevel.ShouldBe(MutationLevel.Complete); + } + + [TestMethod] + public void ShouldMutateDefaultParameter() + { + var source = @" +using System; + +class Program +{ + void Method() + { + DefaultParameterMethod(); + } + + void DefaultParameterMethod(string parameter = ""default"") + { + Console.WriteLine(parameter); + } +}""; +"; + var syntaxTree = CSharpSyntaxTree.ParseText(source); + GetMutations(syntaxTree).ShouldHaveSingleItem(); + } + + [TestMethod] + public void ShouldNotMutateExplicitlyUsedDefaultParameter() + { + var source = @" +using System; + +class Program +{ + void Method() + { + DefaultParameterMethod(""hello world""); + } + + void DefaultParameterMethod(string parameter = ""default"") + { + Console.WriteLine(parameter); + } +}""; +"; + var syntaxTree = CSharpSyntaxTree.ParseText(source); + GetMutations(syntaxTree).ShouldBeEmpty(); + } + + [TestMethod] + public void ShouldNotMutateExplicitlyUsedNamedDefaultParameter() + { + var source = @" +using System; + +class Program +{ + void Method() + { + DefaultParameterMethod(parameter2: ""hello world""); + } + + void DefaultParameterMethod(string parameter1 = ""default1"", string parameter2 = ""default2"", string parameter3 = ""default3"") + { + Console.WriteLine(parameter); + } +}""; +"; + var syntaxTree = CSharpSyntaxTree.ParseText(source); + var mutations = GetMutations(syntaxTree); + + mutations.Count().ShouldBe(2); + mutations.ShouldNotContain(x => x.DisplayName.Contains("parameter2")); + } + + [TestMethod] + public void ShouldMutateImplicitlyUsedDefaultParameter() + { + var source = @" +using System; + +class Program +{ + void Method() + { + DefaultParameterMethod(""Hello world""); + } + + void DefaultParameterMethod(string parameter1 = ""default"", string parameter2 = ""default2"", string parameter3 = ""default3"") + { + Console.WriteLine(parameter); + } +}""; +"; + var syntaxTree = CSharpSyntaxTree.ParseText(source); + + var mutations = GetMutations(syntaxTree); + + mutations.Count().ShouldBe(2); + mutations.ShouldNotContain(x => x.DisplayName.Contains("parameter1")); + } + + private IEnumerable GetMutations(SyntaxTree tree) + { + var invocations = tree + .GetRoot() + .DescendantNodes() + .OfType(); + var options = new StrykerOptions() + { + MutationLevel = MutationLevel.Complete + }; + var orchestratorMock = new Mock(); + orchestratorMock.SetupGet(m => m.Mutators).Returns(new List { new StringMutator() }); + + var target = new DefaultParameterMutator(orchestratorMock.Object, options); + + var semanticModel = GetSemanticModel(tree); + + return invocations.SelectMany(invocation => target.ApplyMutations(invocation, semanticModel)); + } + + private static SemanticModel GetSemanticModel(SyntaxTree tree) + { + var compilation = CSharpCompilation.Create("Test") + .AddReferences(MetadataReference.CreateFromFile(typeof(object).Assembly.Location)) + .AddSyntaxTrees(tree); + return compilation.GetSemanticModel(tree); + } +} diff --git a/src/Stryker.Core/Stryker.Core.UnitTest/Options/Inputs/IgnoreMutationsInputTests.cs b/src/Stryker.Core/Stryker.Core.UnitTest/Options/Inputs/IgnoreMutationsInputTests.cs index 61685c1ba5..d90676cb91 100644 --- a/src/Stryker.Core/Stryker.Core.UnitTest/Options/Inputs/IgnoreMutationsInputTests.cs +++ b/src/Stryker.Core/Stryker.Core.UnitTest/Options/Inputs/IgnoreMutationsInputTests.cs @@ -30,7 +30,7 @@ public void ShouldValidateExcludedMutation() var ex = Should.Throw(() => target.Validate()); - ex.Message.ShouldBe($"Invalid excluded mutation (gibberish). The excluded mutations options are [Statement, Arithmetic, Block, Equality, Boolean, Logical, Assignment, Unary, Update, Checked, Linq, String, Bitwise, Initializer, Regex, NullCoalescing, Math, StringMethod, Conditional]"); + ex.Message.ShouldBe($"Invalid excluded mutation (gibberish). The excluded mutations options are [Statement, Arithmetic, Block, Equality, Boolean, Logical, Assignment, Unary, Update, Checked, Linq, String, Bitwise, Initializer, Regex, NullCoalescing, Math, StringMethod, Conditional, DefaultParameter]"); } [TestMethod] diff --git a/src/Stryker.Core/Stryker.Core/Mutants/CsharpMutantOrchestrator.cs b/src/Stryker.Core/Stryker.Core/Mutants/CSharpMutantOrchestrator.cs similarity index 93% rename from src/Stryker.Core/Stryker.Core/Mutants/CsharpMutantOrchestrator.cs rename to src/Stryker.Core/Stryker.Core/Mutants/CSharpMutantOrchestrator.cs index 4fb782c00d..07d945b12d 100644 --- a/src/Stryker.Core/Stryker.Core/Mutants/CsharpMutantOrchestrator.cs +++ b/src/Stryker.Core/Stryker.Core/Mutants/CSharpMutantOrchestrator.cs @@ -17,27 +17,29 @@ namespace Stryker.Core.Mutants; -/// -public class CsharpMutantOrchestrator : BaseMutantOrchestrator +/// +public class CSharpMutantOrchestrator : BaseMutantOrchestrator, ICSharpMutantOrchestrator { private static readonly TypeBasedStrategy specificOrchestrator = new(); private ILogger Logger { get; } - static CsharpMutantOrchestrator() => + public IEnumerable Mutators { get; } + + static CSharpMutantOrchestrator() => // declare node specific orchestrators. Note that order is relevant, they should be declared from more specific to more generic one specificOrchestrator.RegisterHandlers(BuildOrchestratorList()); /// /// The mutators that should be active during the mutation process /// - public CsharpMutantOrchestrator(MutantPlacer placer, IEnumerable mutators = null, IStrykerOptions options = null) : base(options) + public CSharpMutantOrchestrator(MutantPlacer placer, IEnumerable mutators = null, IStrykerOptions options = null) : base(options) { Placer = placer; Mutators = mutators ?? DefaultMutatorList(); Mutants = new Collection(); - Logger = ApplicationLogging.LoggerFactory.CreateLogger(); + Logger = ApplicationLogging.LoggerFactory.CreateLogger(); } private static List BuildOrchestratorList() => @@ -91,11 +93,12 @@ private static List BuildOrchestratorList() => new SyntaxNodeOrchestrator() ]; - private static List DefaultMutatorList() => + private List DefaultMutatorList() => [ new BinaryExpressionMutator(), new BlockMutator(), new BooleanMutator(), + new DefaultParameterMutator(this, Options), new ConditionalExpressionMutator(), new AssignmentExpressionMutator(), new PrefixUnaryMutator(), @@ -118,8 +121,6 @@ private static List DefaultMutatorList() => new StringMethodMutator() ]; - private IEnumerable Mutators { get; } - public MutantPlacer Placer { get; } /// @@ -132,7 +133,7 @@ public override SyntaxTree Mutate(SyntaxTree input, SemanticModel semanticModel) // search for node specific handler input.WithRootAndOptions(GetHandler(input.GetRoot()).Mutate(input.GetRoot(), semanticModel, new MutationContext(this)), input.Options); - internal INodeOrchestrator GetHandler(SyntaxNode currentNode) => specificOrchestrator.FindHandler(currentNode); + internal static INodeOrchestrator GetHandler(SyntaxNode currentNode) => specificOrchestrator.FindHandler(currentNode); internal IEnumerable GenerateMutationsForNode(SyntaxNode current, SemanticModel semanticModel, MutationContext context) { diff --git a/src/Stryker.Core/Stryker.Core/Mutants/ICSharpMutantOrchestrator.cs b/src/Stryker.Core/Stryker.Core/Mutants/ICSharpMutantOrchestrator.cs new file mode 100644 index 0000000000..9b12162f86 --- /dev/null +++ b/src/Stryker.Core/Stryker.Core/Mutants/ICSharpMutantOrchestrator.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; +using Stryker.Abstractions.Mutators; + +namespace Stryker.Core.Mutants; + +public interface ICSharpMutantOrchestrator +{ + IEnumerable Mutators { get; } +} diff --git a/src/Stryker.Core/Stryker.Core/Mutants/MutationContext.cs b/src/Stryker.Core/Stryker.Core/Mutants/MutationContext.cs index 8d2594b724..8566ae25ad 100644 --- a/src/Stryker.Core/Stryker.Core/Mutants/MutationContext.cs +++ b/src/Stryker.Core/Stryker.Core/Mutants/MutationContext.cs @@ -9,7 +9,7 @@ namespace Stryker.Core.Mutants; /// /// Describe the (syntax tree) context during mutation and ensure proper mutation injection. /// It has several responsibilities: -/// 1) It is in charge of storing mutations as they are generated, inject them at the appropriate syntax level. +/// 1) It is in charge of storing mutations as they are generated, inject them at the appropriate syntax level. /// 2) it also tracks mutator disabled via comments and restores them at adequate times /// /// @@ -17,7 +17,7 @@ internal class MutationContext { // main orchestrator // the orchestrator is used to perform actual mutation injections - private readonly CsharpMutantOrchestrator _mainOrchestrator; + private readonly CSharpMutantOrchestrator _mainOrchestrator; // pending mutation stacks. An entry is pushed in the stack when entering a member or function and popping it when leaving private readonly MutationStore _mutation; @@ -25,7 +25,7 @@ internal class MutationContext /// Mutation context must be created once when starting a mutation process. /// /// - public MutationContext(CsharpMutantOrchestrator mutantOrchestrator) + public MutationContext(CSharpMutantOrchestrator mutantOrchestrator) { _mainOrchestrator = mutantOrchestrator; _mutation = new MutationStore(mutantOrchestrator.Placer); @@ -79,7 +79,7 @@ public IEnumerable GenerateMutantsForNode(SyntaxNode node, SemanticModel /// handler for which to find an orchestrator. /// /// A handler for this node. - public SyntaxNode Mutate(SyntaxNode node, SemanticModel model) => _mainOrchestrator.GetHandler(node).Mutate(node, model, this); + public SyntaxNode Mutate(SyntaxNode node, SemanticModel model) => CSharpMutantOrchestrator.GetHandler(node).Mutate(node, model, this); /// /// Call this to signal mutation occurs in static method or fields diff --git a/src/Stryker.Core/Stryker.Core/MutationTest/CsharpMutationProcess.cs b/src/Stryker.Core/Stryker.Core/MutationTest/CsharpMutationProcess.cs index a5a549b1b4..ed64b4b61c 100644 --- a/src/Stryker.Core/Stryker.Core/MutationTest/CsharpMutationProcess.cs +++ b/src/Stryker.Core/Stryker.Core/MutationTest/CsharpMutationProcess.cs @@ -57,7 +57,7 @@ public CsharpMutationProcess(IStrykerOptions options) : this(null, options) public void Mutate(MutationTestInput input) { var projectInfo = input.SourceProjectInfo.ProjectContents; - var orchestrator = _orchestrator ?? new CsharpMutantOrchestrator(new MutantPlacer(input.SourceProjectInfo.CodeInjector), options: _options); + var orchestrator = _orchestrator ?? new CSharpMutantOrchestrator(new MutantPlacer(input.SourceProjectInfo.CodeInjector), options: _options); var compilingProcess = new CsharpCompilingProcess(input, options: _options); var semanticModels = compilingProcess.GetSemanticModels(projectInfo.GetAllFiles().Cast().Select(x => x.SyntaxTree)); diff --git a/src/Stryker.Core/Stryker.Core/Mutators/DefaultParameterMutator.cs b/src/Stryker.Core/Stryker.Core/Mutators/DefaultParameterMutator.cs new file mode 100644 index 0000000000..26a1090c46 --- /dev/null +++ b/src/Stryker.Core/Stryker.Core/Mutators/DefaultParameterMutator.cs @@ -0,0 +1,100 @@ +using System.Collections.Generic; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Stryker.Abstractions.Mutants; +using Stryker.Abstractions.Mutators; +using Stryker.Abstractions.Options; +using Stryker.Core.Mutants; + +namespace Stryker.Core.Mutators; + +public class DefaultParameterMutator : MutatorBase +{ + private readonly ICSharpMutantOrchestrator _orchestrator; + private readonly IStrykerOptions _options; + + public override MutationLevel MutationLevel => MutationLevel.Complete; + + public DefaultParameterMutator(ICSharpMutantOrchestrator orchestrator, IStrykerOptions options) + { + _orchestrator = orchestrator; + _options = options; + } + + public override IEnumerable ApplyMutations(InvocationExpressionSyntax node, SemanticModel semanticModel) + { + var methodSymbol = (IMethodSymbol)semanticModel.GetSymbolInfo(node).Symbol; + if (methodSymbol is null) + { + yield break; + } + var parameterSymbols = methodSymbol.Parameters; + + var parametersWithDefaultValues = parameterSymbols.Where(p => p.HasExplicitDefaultValue); + + var overridenDefaultParameters = new List(); + foreach (var argument in node.ArgumentList.Arguments) + { + if (argument.NameColon is not null) + { + overridenDefaultParameters.AddRange(parametersWithDefaultValues.Where(p => p.Name == argument.NameColon.Name.Identifier.ValueText)); + } + else + { + overridenDefaultParameters.AddRange(parametersWithDefaultValues.Where(p => p.Ordinal == node.ArgumentList.Arguments.IndexOf(argument))); + } + } + + var unoverridenDefaultParameters = parametersWithDefaultValues.Except(overridenDefaultParameters); + + foreach (var parameter in unoverridenDefaultParameters) + { + var parameterName = parameter.Name; + var argumentNameColon = SyntaxFactory.NameColon(parameterName); + + // If we cannot find the parameter declaration, we should not try to mutate. + if (parameter.DeclaringSyntaxReferences.IsDefaultOrEmpty) + { + yield break; + } + + var parameterSyntaxNode = (ParameterSyntax)parameter.DeclaringSyntaxReferences[0].GetSyntax(); + + var parameterDefaultExpressionNode = parameterSyntaxNode.Default!.Value; + var mutatedDefaultValues = MutateDefaultValueNode(parameterDefaultExpressionNode, semanticModel); + + foreach (var mutatedDefaultValue in mutatedDefaultValues) + { + var namedArgument = SyntaxFactory.Argument(nameColon: argumentNameColon, expression: mutatedDefaultValue, refKindKeyword: default); + + var arguments = node.ArgumentList.Arguments.Add(namedArgument); + var newArgumentList = SyntaxFactory.ArgumentList(arguments); + + yield return new Mutation + { + OriginalNode = node, + ReplacementNode = node.WithArgumentList(newArgumentList), + DisplayName = $"Default parameter mutation: {parameterName}", + Type = Mutator.DefaultParameter + }; + } + } + } + + private IEnumerable MutateDefaultValueNode(SyntaxNode defaultValueNode, SemanticModel semanticModel) + { + List nodeMutations = new(); + foreach (var mutator in _orchestrator.Mutators.Where(m => m is not DefaultParameterMutator)) + { + var mutations = mutator.Mutate(defaultValueNode, semanticModel, _options); + foreach (var mutation in mutations) + { + nodeMutations.Add((ExpressionSyntax)mutation.ReplacementNode); + } + } + + return nodeMutations; + } +}