Skip to content

Commit 5ce07cc

Browse files
fix(mutating): out var in lambdas triggering NRE (#3575)
* fix: out var in lambdas triggereing NRE * chore: copilot fixes --------- Co-authored-by: Rouke Broersma <mobrockers@gmail.com>
1 parent b42be0e commit 5ce07cc

3 files changed

Lines changed: 46 additions & 9 deletions

File tree

src/Stryker.Core/Stryker.Core.UnitTest/Mutants/CsharpMutantOrchestratorTests.cs

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1029,11 +1029,46 @@ public void ShouldNotMutateDefaultValues()
10291029
[TestMethod]
10301030
public void ShouldInitializeOutVars()
10311031
{
1032-
var source = @"public void SomeMethod(out int x, out string text) { x = 1; text = ""hello"";}";
1033-
var expected = @"public void SomeMethod(out int x, out string text) {{x= default(int);text= default(string);}if(StrykerNamespace.MutantControl.IsActive(0)){}else{ x = 1; text = (StrykerNamespace.MutantControl.IsActive(1)?"""":""hello"");
1034-
}}";
1032+
const string Source = """public void SomeMethod(out int x, out string text) { x = 1; text = "hello";}""";
1033+
const string Expected = """
1034+
public void SomeMethod(out int x, out string text) {{x= default(int);text= default(string);}if(StrykerNamespace.MutantControl.IsActive(0)){}else{ x = 1; text = (StrykerNamespace.MutantControl.IsActive(1)?"":"hello");
1035+
}}
1036+
""";
10351037

1036-
ShouldMutateSourceInClassToExpected(source, expected);
1038+
ShouldMutateSourceInClassToExpected(Source, Expected);
1039+
}
1040+
1041+
[TestMethod]
1042+
public void ShouldInitializeOutVarsForLambdas()
1043+
{
1044+
const string Source = """
1045+
public static void Thing()
1046+
{
1047+
var results = new List<ExceptionResultDelegate>();
1048+
1049+
results.Add((out result) =>
1050+
{
1051+
result = false;
1052+
return false;
1053+
});
1054+
}
1055+
""";
1056+
const string Expected = """
1057+
public static void Thing()
1058+
{if(StrykerNamespace.MutantControl.IsActive(0)){}
1059+
else{
1060+
var results = new List<ExceptionResultDelegate>();
1061+
1062+
if(StrykerNamespace.MutantControl.IsActive(1)){;}else{results.Add((out result) =>
1063+
{{result= default;} if(StrykerNamespace.MutantControl.IsActive(2)) {}else{
1064+
result = (StrykerNamespace.MutantControl.IsActive(3)?true:false);
1065+
return (StrykerNamespace.MutantControl.IsActive(4)?true:false);
1066+
}return default;});}
1067+
}
1068+
}
1069+
""";
1070+
1071+
ShouldMutateSourceInClassToExpected(Source, Expected);
10371072
}
10381073

10391074
[TestMethod]

src/Stryker.Core/Stryker.Core/Helpers/RoslynHelper.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,16 +141,18 @@ public static AccessorDeclarationSyntax GetAccessor(this PropertyDeclarationSynt
141141
/// Return a default(type) expression.
142142
/// </summary>
143143
/// <param name="type">Type used in the resulting default expression.</param>
144-
/// <returns>An expression representing `default(<paramref name="type"/>'.</returns>
145-
public static ExpressionSyntax BuildDefaultExpression(this TypeSyntax type)
146-
=> SyntaxFactory.DefaultExpression(type.WithoutTrivia()).WithLeadingTrivia(SyntaxFactory.Space);
144+
/// <returns>An expression representing `default(<paramref name="type"/>`.</returns>
145+
public static ExpressionSyntax BuildDefaultExpression(this TypeSyntax type) =>
146+
(type == null
147+
? (ExpressionSyntax) SyntaxFactory.LiteralExpression(SyntaxKind.DefaultLiteralExpression)
148+
: SyntaxFactory.DefaultExpression(type.WithoutTrivia())).WithLeadingTrivia(SyntaxFactory.Space);
147149

148150
/// <summary>
149151
/// Check if a statements (or one of its child statements, if any) verifies some given predicate.
150152
/// </summary>
151153
/// <param name="syntax">initial statements</param>
152154
/// <param name="predicate">predicate to verify</param>
153-
/// <param name="skipBlocks">does not parse block statements if true</param>
155+
/// <param name="skipBlocks">true to skip syntax blocks</param>
154156
/// <returns>true if any of the child statements verify the predicate</returns>
155157
/// <remarks>scanning stops as soon as one child matches the predicate</remarks>
156158
public static bool ScanChildStatements(this StatementSyntax syntax, Func<StatementSyntax, bool> predicate, bool skipBlocks = false)

src/Stryker.Core/Stryker.Core/Instrumentation/DefaultInitializationEngine.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public BlockSyntax InjectOutParametersInitialization(BlockSyntax body, IEnumerab
4848
}
4949
else
5050
{
51-
// this is the first initializer helper, no pre-existing ones
51+
// this is the first initializer helper, no pre existing ones
5252
initializers = [];
5353
// keep all statements
5454
originalStatements = body.Statements;

0 commit comments

Comments
 (0)