From 822a0e8661b4ace3229c19490c2a2fb81558da25 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Mon, 1 Jun 2026 09:40:22 +0100 Subject: [PATCH 1/3] fix(sourcegen): fully-qualify Linq calls in params array binding (#6140) The dynamic-count params/array binding emitted `Enumerable.Range(...).Select(...).ToArray()` using the extension-method chain. Generated `.g.cs` files only import `System.Linq` when the project has `ImplicitUsings` enabled, so projects without it failed to compile with CS1061 ("IEnumerable does not contain a definition for Select"). Rewrite as fully-qualified static calls `Enumerable.ToArray(Enumerable.Select(Enumerable.Range(...), ...))` so the generated code compiles regardless of the consuming project's usings. Regressed in #6122. Snapshots regenerated. --- .../ArgsAsArrayTests.Test.verified.txt | 20 +++++++++---------- ...nflictingNamespace.DotNet10_0.verified.txt | 2 +- ...onflictingNamespace.DotNet8_0.verified.txt | 2 +- ...onflictingNamespace.DotNet9_0.verified.txt | 2 +- ...thConflictingNamespace.Net4_7.verified.txt | 2 +- ...nflictingNamespace.DotNet10_0.verified.txt | 2 +- ...onflictingNamespace.DotNet8_0.verified.txt | 2 +- ...onflictingNamespace.DotNet9_0.verified.txt | 2 +- ...thConflictingNamespace.Net4_7.verified.txt | 2 +- .../DataDrivenTests.Test.verified.txt | 4 ++-- ...hodDataSourceDrivenTests.Test.verified.txt | 4 ++-- .../Tests2112.Test.verified.txt | 4 ++-- .../Helpers/TupleArgumentHelper.cs | 5 ++++- 13 files changed, 28 insertions(+), 25 deletions(-) diff --git a/TUnit.Core.SourceGenerator.Tests/ArgsAsArrayTests.Test.verified.txt b/TUnit.Core.SourceGenerator.Tests/ArgsAsArrayTests.Test.verified.txt index 3b7a6fa3f8..a1483ca69e 100644 --- a/TUnit.Core.SourceGenerator.Tests/ArgsAsArrayTests.Test.verified.txt +++ b/TUnit.Core.SourceGenerator.Tests/ArgsAsArrayTests.Test.verified.txt @@ -1,4 +1,4 @@ -// +// #pragma warning disable #nullable enable @@ -106,7 +106,7 @@ global::TUnit.Core.ParameterMetadataFactory.Create(typeof(string[]), "names", ne return default(global::System.Threading.Tasks.ValueTask); } default: - instance.Params((args.Length > 0 ? global::System.Linq.Enumerable.Range(0, args.Length).Select(i => global::TUnit.Core.Helpers.CastHelper.Cast(args[i])).ToArray() : new string[0])); + instance.Params((args.Length > 0 ? global::System.Linq.Enumerable.ToArray(global::System.Linq.Enumerable.Select(global::System.Linq.Enumerable.Range(0, args.Length), i => global::TUnit.Core.Helpers.CastHelper.Cast(args[i]))) : new string[0])); return default(global::System.Threading.Tasks.ValueTask); } } @@ -157,7 +157,7 @@ global::TUnit.Core.ParameterMetadataFactory.Create(typeof(string[]), "names", ne return default(global::System.Threading.Tasks.ValueTask); } default: - instance.ParamsEnumerable((args.Length > 0 ? global::System.Linq.Enumerable.Range(0, args.Length).Select(i => global::TUnit.Core.Helpers.CastHelper.Cast(args[i])).ToArray() : new string[0])); + instance.ParamsEnumerable((args.Length > 0 ? global::System.Linq.Enumerable.ToArray(global::System.Linq.Enumerable.Select(global::System.Linq.Enumerable.Range(0, args.Length), i => global::TUnit.Core.Helpers.CastHelper.Cast(args[i]))) : new string[0])); return default(global::System.Threading.Tasks.ValueTask); } } @@ -212,7 +212,7 @@ global::TUnit.Core.ParameterMetadataFactory.Create(typeof(string[]), "names", ne { throw new global::System.ArgumentException($"Expected at least 1 argument, but got {args.Length}"); } - instance.Following_Non_Params(global::TUnit.Core.Helpers.CastHelper.Cast(args[0]), (args.Length > 1 ? global::System.Linq.Enumerable.Range(1, args.Length - 1).Select(i => global::TUnit.Core.Helpers.CastHelper.Cast(args[i])).ToArray() : new string[0])); + instance.Following_Non_Params(global::TUnit.Core.Helpers.CastHelper.Cast(args[0]), (args.Length > 1 ? global::System.Linq.Enumerable.ToArray(global::System.Linq.Enumerable.Select(global::System.Linq.Enumerable.Range(1, args.Length - 1), i => global::TUnit.Core.Helpers.CastHelper.Cast(args[i]))) : new string[0])); return default(global::System.Threading.Tasks.ValueTask); } } @@ -256,7 +256,7 @@ global::TUnit.Core.ParameterMetadataFactory.Create(typeof(string[]), "names", ne return new global::System.Threading.Tasks.ValueTask(instance.NonParamsStringArray(new string[] { global::TUnit.Core.Helpers.CastHelper.Cast(args[0]), global::TUnit.Core.Helpers.CastHelper.Cast(args[1]), global::TUnit.Core.Helpers.CastHelper.Cast(args[2]), global::TUnit.Core.Helpers.CastHelper.Cast(args[3]), global::TUnit.Core.Helpers.CastHelper.Cast(args[4]), global::TUnit.Core.Helpers.CastHelper.Cast(args[5]) })); } default: - return new global::System.Threading.Tasks.ValueTask(instance.NonParamsStringArray((args.Length > 0 ? global::System.Linq.Enumerable.Range(0, args.Length).Select(i => global::TUnit.Core.Helpers.CastHelper.Cast(args[i])).ToArray() : new string[0]))); + return new global::System.Threading.Tasks.ValueTask(instance.NonParamsStringArray((args.Length > 0 ? global::System.Linq.Enumerable.ToArray(global::System.Linq.Enumerable.Select(global::System.Linq.Enumerable.Range(0, args.Length), i => global::TUnit.Core.Helpers.CastHelper.Cast(args[i]))) : new string[0]))); } } catch (global::System.Exception ex) @@ -299,7 +299,7 @@ global::TUnit.Core.ParameterMetadataFactory.Create(typeof(string[]), "names", ne return new global::System.Threading.Tasks.ValueTask(instance.NonParamsStringArray_SingleValue(new string[] { global::TUnit.Core.Helpers.CastHelper.Cast(args[0]), global::TUnit.Core.Helpers.CastHelper.Cast(args[1]), global::TUnit.Core.Helpers.CastHelper.Cast(args[2]), global::TUnit.Core.Helpers.CastHelper.Cast(args[3]), global::TUnit.Core.Helpers.CastHelper.Cast(args[4]), global::TUnit.Core.Helpers.CastHelper.Cast(args[5]) })); } default: - return new global::System.Threading.Tasks.ValueTask(instance.NonParamsStringArray_SingleValue((args.Length > 0 ? global::System.Linq.Enumerable.Range(0, args.Length).Select(i => global::TUnit.Core.Helpers.CastHelper.Cast(args[i])).ToArray() : new string[0]))); + return new global::System.Threading.Tasks.ValueTask(instance.NonParamsStringArray_SingleValue((args.Length > 0 ? global::System.Linq.Enumerable.ToArray(global::System.Linq.Enumerable.Select(global::System.Linq.Enumerable.Range(0, args.Length), i => global::TUnit.Core.Helpers.CastHelper.Cast(args[i]))) : new string[0]))); } } catch (global::System.Exception ex) @@ -342,7 +342,7 @@ global::TUnit.Core.ParameterMetadataFactory.Create(typeof(string[]), "names", ne return new global::System.Threading.Tasks.ValueTask(instance.NonParamsStringArray_MultipleValues(new string[] { global::TUnit.Core.Helpers.CastHelper.Cast(args[0]), global::TUnit.Core.Helpers.CastHelper.Cast(args[1]), global::TUnit.Core.Helpers.CastHelper.Cast(args[2]), global::TUnit.Core.Helpers.CastHelper.Cast(args[3]), global::TUnit.Core.Helpers.CastHelper.Cast(args[4]), global::TUnit.Core.Helpers.CastHelper.Cast(args[5]) })); } default: - return new global::System.Threading.Tasks.ValueTask(instance.NonParamsStringArray_MultipleValues((args.Length > 0 ? global::System.Linq.Enumerable.Range(0, args.Length).Select(i => global::TUnit.Core.Helpers.CastHelper.Cast(args[i])).ToArray() : new string[0]))); + return new global::System.Threading.Tasks.ValueTask(instance.NonParamsStringArray_MultipleValues((args.Length > 0 ? global::System.Linq.Enumerable.ToArray(global::System.Linq.Enumerable.Select(global::System.Linq.Enumerable.Range(0, args.Length), i => global::TUnit.Core.Helpers.CastHelper.Cast(args[i]))) : new string[0]))); } } catch (global::System.Exception ex) @@ -385,7 +385,7 @@ global::TUnit.Core.ParameterMetadataFactory.Create(typeof(string[]), "names", ne return new global::System.Threading.Tasks.ValueTask(instance.NonParamsIntArray(new int[] { global::TUnit.Core.Helpers.CastHelper.Cast(args[0]), global::TUnit.Core.Helpers.CastHelper.Cast(args[1]), global::TUnit.Core.Helpers.CastHelper.Cast(args[2]), global::TUnit.Core.Helpers.CastHelper.Cast(args[3]), global::TUnit.Core.Helpers.CastHelper.Cast(args[4]), global::TUnit.Core.Helpers.CastHelper.Cast(args[5]) })); } default: - return new global::System.Threading.Tasks.ValueTask(instance.NonParamsIntArray((args.Length > 0 ? global::System.Linq.Enumerable.Range(0, args.Length).Select(i => global::TUnit.Core.Helpers.CastHelper.Cast(args[i])).ToArray() : new int[0]))); + return new global::System.Threading.Tasks.ValueTask(instance.NonParamsIntArray((args.Length > 0 ? global::System.Linq.Enumerable.ToArray(global::System.Linq.Enumerable.Select(global::System.Linq.Enumerable.Range(0, args.Length), i => global::TUnit.Core.Helpers.CastHelper.Cast(args[i]))) : new int[0]))); } } catch (global::System.Exception ex) @@ -428,7 +428,7 @@ global::TUnit.Core.ParameterMetadataFactory.Create(typeof(string[]), "names", ne return new global::System.Threading.Tasks.ValueTask(instance.GenericStringArray(new string[] { global::TUnit.Core.Helpers.CastHelper.Cast(args[0]), global::TUnit.Core.Helpers.CastHelper.Cast(args[1]), global::TUnit.Core.Helpers.CastHelper.Cast(args[2]), global::TUnit.Core.Helpers.CastHelper.Cast(args[3]), global::TUnit.Core.Helpers.CastHelper.Cast(args[4]), global::TUnit.Core.Helpers.CastHelper.Cast(args[5]) })); } default: - return new global::System.Threading.Tasks.ValueTask(instance.GenericStringArray((args.Length > 0 ? global::System.Linq.Enumerable.Range(0, args.Length).Select(i => global::TUnit.Core.Helpers.CastHelper.Cast(args[i])).ToArray() : new string[0]))); + return new global::System.Threading.Tasks.ValueTask(instance.GenericStringArray((args.Length > 0 ? global::System.Linq.Enumerable.ToArray(global::System.Linq.Enumerable.Select(global::System.Linq.Enumerable.Range(0, args.Length), i => global::TUnit.Core.Helpers.CastHelper.Cast(args[i]))) : new string[0]))); } } catch (global::System.Exception ex) @@ -471,7 +471,7 @@ global::TUnit.Core.ParameterMetadataFactory.Create(typeof(string[]), "names", ne return new global::System.Threading.Tasks.ValueTask(instance.NonParamsStringArray_BeyondStaticCaseCap(new string[] { global::TUnit.Core.Helpers.CastHelper.Cast(args[0]), global::TUnit.Core.Helpers.CastHelper.Cast(args[1]), global::TUnit.Core.Helpers.CastHelper.Cast(args[2]), global::TUnit.Core.Helpers.CastHelper.Cast(args[3]), global::TUnit.Core.Helpers.CastHelper.Cast(args[4]), global::TUnit.Core.Helpers.CastHelper.Cast(args[5]) })); } default: - return new global::System.Threading.Tasks.ValueTask(instance.NonParamsStringArray_BeyondStaticCaseCap((args.Length > 0 ? global::System.Linq.Enumerable.Range(0, args.Length).Select(i => global::TUnit.Core.Helpers.CastHelper.Cast(args[i])).ToArray() : new string[0]))); + return new global::System.Threading.Tasks.ValueTask(instance.NonParamsStringArray_BeyondStaticCaseCap((args.Length > 0 ? global::System.Linq.Enumerable.ToArray(global::System.Linq.Enumerable.Select(global::System.Linq.Enumerable.Range(0, args.Length), i => global::TUnit.Core.Helpers.CastHelper.Cast(args[i]))) : new string[0]))); } } catch (global::System.Exception ex) diff --git a/TUnit.Core.SourceGenerator.Tests/ConflictingNamespaceTests.DataDrivenTest_WithConflictingNamespace.DotNet10_0.verified.txt b/TUnit.Core.SourceGenerator.Tests/ConflictingNamespaceTests.DataDrivenTest_WithConflictingNamespace.DotNet10_0.verified.txt index 4aa3da7152..8cab517356 100644 --- a/TUnit.Core.SourceGenerator.Tests/ConflictingNamespaceTests.DataDrivenTest_WithConflictingNamespace.DotNet10_0.verified.txt +++ b/TUnit.Core.SourceGenerator.Tests/ConflictingNamespaceTests.DataDrivenTest_WithConflictingNamespace.DotNet10_0.verified.txt @@ -270,7 +270,7 @@ global::TUnit.Core.ParameterMetadataFactory.Create(typeof(int), "value", new glo return default(global::System.Threading.Tasks.ValueTask); } default: - instance.IntegerArray((args.Length > 0 ? global::System.Linq.Enumerable.Range(0, args.Length).Select(i => global::TUnit.Core.Helpers.CastHelper.Cast(args[i])).ToArray() : new int[0])); + instance.IntegerArray((args.Length > 0 ? global::System.Linq.Enumerable.ToArray(global::System.Linq.Enumerable.Select(global::System.Linq.Enumerable.Range(0, args.Length), i => global::TUnit.Core.Helpers.CastHelper.Cast(args[i]))) : new int[0])); return default(global::System.Threading.Tasks.ValueTask); } } diff --git a/TUnit.Core.SourceGenerator.Tests/ConflictingNamespaceTests.DataDrivenTest_WithConflictingNamespace.DotNet8_0.verified.txt b/TUnit.Core.SourceGenerator.Tests/ConflictingNamespaceTests.DataDrivenTest_WithConflictingNamespace.DotNet8_0.verified.txt index 4aa3da7152..8cab517356 100644 --- a/TUnit.Core.SourceGenerator.Tests/ConflictingNamespaceTests.DataDrivenTest_WithConflictingNamespace.DotNet8_0.verified.txt +++ b/TUnit.Core.SourceGenerator.Tests/ConflictingNamespaceTests.DataDrivenTest_WithConflictingNamespace.DotNet8_0.verified.txt @@ -270,7 +270,7 @@ global::TUnit.Core.ParameterMetadataFactory.Create(typeof(int), "value", new glo return default(global::System.Threading.Tasks.ValueTask); } default: - instance.IntegerArray((args.Length > 0 ? global::System.Linq.Enumerable.Range(0, args.Length).Select(i => global::TUnit.Core.Helpers.CastHelper.Cast(args[i])).ToArray() : new int[0])); + instance.IntegerArray((args.Length > 0 ? global::System.Linq.Enumerable.ToArray(global::System.Linq.Enumerable.Select(global::System.Linq.Enumerable.Range(0, args.Length), i => global::TUnit.Core.Helpers.CastHelper.Cast(args[i]))) : new int[0])); return default(global::System.Threading.Tasks.ValueTask); } } diff --git a/TUnit.Core.SourceGenerator.Tests/ConflictingNamespaceTests.DataDrivenTest_WithConflictingNamespace.DotNet9_0.verified.txt b/TUnit.Core.SourceGenerator.Tests/ConflictingNamespaceTests.DataDrivenTest_WithConflictingNamespace.DotNet9_0.verified.txt index 4aa3da7152..8cab517356 100644 --- a/TUnit.Core.SourceGenerator.Tests/ConflictingNamespaceTests.DataDrivenTest_WithConflictingNamespace.DotNet9_0.verified.txt +++ b/TUnit.Core.SourceGenerator.Tests/ConflictingNamespaceTests.DataDrivenTest_WithConflictingNamespace.DotNet9_0.verified.txt @@ -270,7 +270,7 @@ global::TUnit.Core.ParameterMetadataFactory.Create(typeof(int), "value", new glo return default(global::System.Threading.Tasks.ValueTask); } default: - instance.IntegerArray((args.Length > 0 ? global::System.Linq.Enumerable.Range(0, args.Length).Select(i => global::TUnit.Core.Helpers.CastHelper.Cast(args[i])).ToArray() : new int[0])); + instance.IntegerArray((args.Length > 0 ? global::System.Linq.Enumerable.ToArray(global::System.Linq.Enumerable.Select(global::System.Linq.Enumerable.Range(0, args.Length), i => global::TUnit.Core.Helpers.CastHelper.Cast(args[i]))) : new int[0])); return default(global::System.Threading.Tasks.ValueTask); } } diff --git a/TUnit.Core.SourceGenerator.Tests/ConflictingNamespaceTests.DataDrivenTest_WithConflictingNamespace.Net4_7.verified.txt b/TUnit.Core.SourceGenerator.Tests/ConflictingNamespaceTests.DataDrivenTest_WithConflictingNamespace.Net4_7.verified.txt index 99ac2392d3..a6f62b8b2d 100644 --- a/TUnit.Core.SourceGenerator.Tests/ConflictingNamespaceTests.DataDrivenTest_WithConflictingNamespace.Net4_7.verified.txt +++ b/TUnit.Core.SourceGenerator.Tests/ConflictingNamespaceTests.DataDrivenTest_WithConflictingNamespace.Net4_7.verified.txt @@ -270,7 +270,7 @@ global::TUnit.Core.ParameterMetadataFactory.Create(typeof(int), "value", new glo return default(global::System.Threading.Tasks.ValueTask); } default: - instance.IntegerArray((args.Length > 0 ? global::System.Linq.Enumerable.Range(0, args.Length).Select(i => global::TUnit.Core.Helpers.CastHelper.Cast(args[i])).ToArray() : new int[0])); + instance.IntegerArray((args.Length > 0 ? global::System.Linq.Enumerable.ToArray(global::System.Linq.Enumerable.Select(global::System.Linq.Enumerable.Range(0, args.Length), i => global::TUnit.Core.Helpers.CastHelper.Cast(args[i]))) : new int[0])); return default(global::System.Threading.Tasks.ValueTask); } } diff --git a/TUnit.Core.SourceGenerator.Tests/ConflictingNamespaceTests.MethodDataSource_WithConflictingNamespace.DotNet10_0.verified.txt b/TUnit.Core.SourceGenerator.Tests/ConflictingNamespaceTests.MethodDataSource_WithConflictingNamespace.DotNet10_0.verified.txt index 6033af57e1..00c233071c 100644 --- a/TUnit.Core.SourceGenerator.Tests/ConflictingNamespaceTests.MethodDataSource_WithConflictingNamespace.DotNet10_0.verified.txt +++ b/TUnit.Core.SourceGenerator.Tests/ConflictingNamespaceTests.MethodDataSource_WithConflictingNamespace.DotNet10_0.verified.txt @@ -210,7 +210,7 @@ global::TUnit.Core.ParameterMetadataFactory.Create(typeof(string[]), "strings", return new global::System.Threading.Tasks.ValueTask(instance.EnumerableFuncArrayTest(new string[] { global::TUnit.Core.Helpers.CastHelper.Cast(args[0]), global::TUnit.Core.Helpers.CastHelper.Cast(args[1]), global::TUnit.Core.Helpers.CastHelper.Cast(args[2]), global::TUnit.Core.Helpers.CastHelper.Cast(args[3]), global::TUnit.Core.Helpers.CastHelper.Cast(args[4]), global::TUnit.Core.Helpers.CastHelper.Cast(args[5]) })); } default: - return new global::System.Threading.Tasks.ValueTask(instance.EnumerableFuncArrayTest((args.Length > 0 ? global::System.Linq.Enumerable.Range(0, args.Length).Select(i => global::TUnit.Core.Helpers.CastHelper.Cast(args[i])).ToArray() : new string[0]))); + return new global::System.Threading.Tasks.ValueTask(instance.EnumerableFuncArrayTest((args.Length > 0 ? global::System.Linq.Enumerable.ToArray(global::System.Linq.Enumerable.Select(global::System.Linq.Enumerable.Range(0, args.Length), i => global::TUnit.Core.Helpers.CastHelper.Cast(args[i]))) : new string[0]))); } } catch (global::System.Exception ex) diff --git a/TUnit.Core.SourceGenerator.Tests/ConflictingNamespaceTests.MethodDataSource_WithConflictingNamespace.DotNet8_0.verified.txt b/TUnit.Core.SourceGenerator.Tests/ConflictingNamespaceTests.MethodDataSource_WithConflictingNamespace.DotNet8_0.verified.txt index 6033af57e1..00c233071c 100644 --- a/TUnit.Core.SourceGenerator.Tests/ConflictingNamespaceTests.MethodDataSource_WithConflictingNamespace.DotNet8_0.verified.txt +++ b/TUnit.Core.SourceGenerator.Tests/ConflictingNamespaceTests.MethodDataSource_WithConflictingNamespace.DotNet8_0.verified.txt @@ -210,7 +210,7 @@ global::TUnit.Core.ParameterMetadataFactory.Create(typeof(string[]), "strings", return new global::System.Threading.Tasks.ValueTask(instance.EnumerableFuncArrayTest(new string[] { global::TUnit.Core.Helpers.CastHelper.Cast(args[0]), global::TUnit.Core.Helpers.CastHelper.Cast(args[1]), global::TUnit.Core.Helpers.CastHelper.Cast(args[2]), global::TUnit.Core.Helpers.CastHelper.Cast(args[3]), global::TUnit.Core.Helpers.CastHelper.Cast(args[4]), global::TUnit.Core.Helpers.CastHelper.Cast(args[5]) })); } default: - return new global::System.Threading.Tasks.ValueTask(instance.EnumerableFuncArrayTest((args.Length > 0 ? global::System.Linq.Enumerable.Range(0, args.Length).Select(i => global::TUnit.Core.Helpers.CastHelper.Cast(args[i])).ToArray() : new string[0]))); + return new global::System.Threading.Tasks.ValueTask(instance.EnumerableFuncArrayTest((args.Length > 0 ? global::System.Linq.Enumerable.ToArray(global::System.Linq.Enumerable.Select(global::System.Linq.Enumerable.Range(0, args.Length), i => global::TUnit.Core.Helpers.CastHelper.Cast(args[i]))) : new string[0]))); } } catch (global::System.Exception ex) diff --git a/TUnit.Core.SourceGenerator.Tests/ConflictingNamespaceTests.MethodDataSource_WithConflictingNamespace.DotNet9_0.verified.txt b/TUnit.Core.SourceGenerator.Tests/ConflictingNamespaceTests.MethodDataSource_WithConflictingNamespace.DotNet9_0.verified.txt index 6033af57e1..00c233071c 100644 --- a/TUnit.Core.SourceGenerator.Tests/ConflictingNamespaceTests.MethodDataSource_WithConflictingNamespace.DotNet9_0.verified.txt +++ b/TUnit.Core.SourceGenerator.Tests/ConflictingNamespaceTests.MethodDataSource_WithConflictingNamespace.DotNet9_0.verified.txt @@ -210,7 +210,7 @@ global::TUnit.Core.ParameterMetadataFactory.Create(typeof(string[]), "strings", return new global::System.Threading.Tasks.ValueTask(instance.EnumerableFuncArrayTest(new string[] { global::TUnit.Core.Helpers.CastHelper.Cast(args[0]), global::TUnit.Core.Helpers.CastHelper.Cast(args[1]), global::TUnit.Core.Helpers.CastHelper.Cast(args[2]), global::TUnit.Core.Helpers.CastHelper.Cast(args[3]), global::TUnit.Core.Helpers.CastHelper.Cast(args[4]), global::TUnit.Core.Helpers.CastHelper.Cast(args[5]) })); } default: - return new global::System.Threading.Tasks.ValueTask(instance.EnumerableFuncArrayTest((args.Length > 0 ? global::System.Linq.Enumerable.Range(0, args.Length).Select(i => global::TUnit.Core.Helpers.CastHelper.Cast(args[i])).ToArray() : new string[0]))); + return new global::System.Threading.Tasks.ValueTask(instance.EnumerableFuncArrayTest((args.Length > 0 ? global::System.Linq.Enumerable.ToArray(global::System.Linq.Enumerable.Select(global::System.Linq.Enumerable.Range(0, args.Length), i => global::TUnit.Core.Helpers.CastHelper.Cast(args[i]))) : new string[0]))); } } catch (global::System.Exception ex) diff --git a/TUnit.Core.SourceGenerator.Tests/ConflictingNamespaceTests.MethodDataSource_WithConflictingNamespace.Net4_7.verified.txt b/TUnit.Core.SourceGenerator.Tests/ConflictingNamespaceTests.MethodDataSource_WithConflictingNamespace.Net4_7.verified.txt index 74e3dfcc0e..b4e7d92b5d 100644 --- a/TUnit.Core.SourceGenerator.Tests/ConflictingNamespaceTests.MethodDataSource_WithConflictingNamespace.Net4_7.verified.txt +++ b/TUnit.Core.SourceGenerator.Tests/ConflictingNamespaceTests.MethodDataSource_WithConflictingNamespace.Net4_7.verified.txt @@ -210,7 +210,7 @@ global::TUnit.Core.ParameterMetadataFactory.Create(typeof(string[]), "strings", return new global::System.Threading.Tasks.ValueTask(instance.EnumerableFuncArrayTest(new string[] { global::TUnit.Core.Helpers.CastHelper.Cast(args[0]), global::TUnit.Core.Helpers.CastHelper.Cast(args[1]), global::TUnit.Core.Helpers.CastHelper.Cast(args[2]), global::TUnit.Core.Helpers.CastHelper.Cast(args[3]), global::TUnit.Core.Helpers.CastHelper.Cast(args[4]), global::TUnit.Core.Helpers.CastHelper.Cast(args[5]) })); } default: - return new global::System.Threading.Tasks.ValueTask(instance.EnumerableFuncArrayTest((args.Length > 0 ? global::System.Linq.Enumerable.Range(0, args.Length).Select(i => global::TUnit.Core.Helpers.CastHelper.Cast(args[i])).ToArray() : new string[0]))); + return new global::System.Threading.Tasks.ValueTask(instance.EnumerableFuncArrayTest((args.Length > 0 ? global::System.Linq.Enumerable.ToArray(global::System.Linq.Enumerable.Select(global::System.Linq.Enumerable.Range(0, args.Length), i => global::TUnit.Core.Helpers.CastHelper.Cast(args[i]))) : new string[0]))); } } catch (global::System.Exception ex) diff --git a/TUnit.Core.SourceGenerator.Tests/DataDrivenTests.Test.verified.txt b/TUnit.Core.SourceGenerator.Tests/DataDrivenTests.Test.verified.txt index 99ac2392d3..8cab517356 100644 --- a/TUnit.Core.SourceGenerator.Tests/DataDrivenTests.Test.verified.txt +++ b/TUnit.Core.SourceGenerator.Tests/DataDrivenTests.Test.verified.txt @@ -1,4 +1,4 @@ -// +// #pragma warning disable #nullable enable @@ -270,7 +270,7 @@ global::TUnit.Core.ParameterMetadataFactory.Create(typeof(int), "value", new glo return default(global::System.Threading.Tasks.ValueTask); } default: - instance.IntegerArray((args.Length > 0 ? global::System.Linq.Enumerable.Range(0, args.Length).Select(i => global::TUnit.Core.Helpers.CastHelper.Cast(args[i])).ToArray() : new int[0])); + instance.IntegerArray((args.Length > 0 ? global::System.Linq.Enumerable.ToArray(global::System.Linq.Enumerable.Select(global::System.Linq.Enumerable.Range(0, args.Length), i => global::TUnit.Core.Helpers.CastHelper.Cast(args[i]))) : new int[0])); return default(global::System.Threading.Tasks.ValueTask); } } diff --git a/TUnit.Core.SourceGenerator.Tests/MethodDataSourceDrivenTests.Test.verified.txt b/TUnit.Core.SourceGenerator.Tests/MethodDataSourceDrivenTests.Test.verified.txt index 74e3dfcc0e..00c233071c 100644 --- a/TUnit.Core.SourceGenerator.Tests/MethodDataSourceDrivenTests.Test.verified.txt +++ b/TUnit.Core.SourceGenerator.Tests/MethodDataSourceDrivenTests.Test.verified.txt @@ -1,4 +1,4 @@ -// +// #pragma warning disable #nullable enable @@ -210,7 +210,7 @@ global::TUnit.Core.ParameterMetadataFactory.Create(typeof(string[]), "strings", return new global::System.Threading.Tasks.ValueTask(instance.EnumerableFuncArrayTest(new string[] { global::TUnit.Core.Helpers.CastHelper.Cast(args[0]), global::TUnit.Core.Helpers.CastHelper.Cast(args[1]), global::TUnit.Core.Helpers.CastHelper.Cast(args[2]), global::TUnit.Core.Helpers.CastHelper.Cast(args[3]), global::TUnit.Core.Helpers.CastHelper.Cast(args[4]), global::TUnit.Core.Helpers.CastHelper.Cast(args[5]) })); } default: - return new global::System.Threading.Tasks.ValueTask(instance.EnumerableFuncArrayTest((args.Length > 0 ? global::System.Linq.Enumerable.Range(0, args.Length).Select(i => global::TUnit.Core.Helpers.CastHelper.Cast(args[i])).ToArray() : new string[0]))); + return new global::System.Threading.Tasks.ValueTask(instance.EnumerableFuncArrayTest((args.Length > 0 ? global::System.Linq.Enumerable.ToArray(global::System.Linq.Enumerable.Select(global::System.Linq.Enumerable.Range(0, args.Length), i => global::TUnit.Core.Helpers.CastHelper.Cast(args[i]))) : new string[0]))); } } catch (global::System.Exception ex) diff --git a/TUnit.Core.SourceGenerator.Tests/Tests2112.Test.verified.txt b/TUnit.Core.SourceGenerator.Tests/Tests2112.Test.verified.txt index 7603da9ba8..32ef232545 100644 --- a/TUnit.Core.SourceGenerator.Tests/Tests2112.Test.verified.txt +++ b/TUnit.Core.SourceGenerator.Tests/Tests2112.Test.verified.txt @@ -83,7 +83,7 @@ global::TUnit.Core.ParameterMetadataFactory.Create(typeof(long[]), "arr", new gl { throw new global::System.ArgumentException($"Expected at least 1 argument, but got {args.Length}"); } - instance.Test(global::TUnit.Core.Helpers.CastHelper.Cast(args[0]), (args.Length > 1 ? global::System.Linq.Enumerable.Range(1, args.Length - 1).Select(i => global::TUnit.Core.Helpers.CastHelper.Cast(args[i])).ToArray() : new long[0])); + instance.Test(global::TUnit.Core.Helpers.CastHelper.Cast(args[0]), (args.Length > 1 ? global::System.Linq.Enumerable.ToArray(global::System.Linq.Enumerable.Select(global::System.Linq.Enumerable.Range(1, args.Length - 1), i => global::TUnit.Core.Helpers.CastHelper.Cast(args[i]))) : new long[0])); return default(global::System.Threading.Tasks.ValueTask); } } @@ -138,7 +138,7 @@ global::TUnit.Core.ParameterMetadataFactory.Create(typeof(long[]), "arr", new gl { throw new global::System.ArgumentException($"Expected at least 1 argument, but got {args.Length}"); } - instance.Test2(global::TUnit.Core.Helpers.CastHelper.Cast(args[0]), (args.Length > 1 ? global::System.Linq.Enumerable.Range(1, args.Length - 1).Select(i => global::TUnit.Core.Helpers.CastHelper.Cast(args[i])).ToArray() : new long[0])); + instance.Test2(global::TUnit.Core.Helpers.CastHelper.Cast(args[0]), (args.Length > 1 ? global::System.Linq.Enumerable.ToArray(global::System.Linq.Enumerable.Select(global::System.Linq.Enumerable.Range(1, args.Length - 1), i => global::TUnit.Core.Helpers.CastHelper.Cast(args[i]))) : new long[0])); return default(global::System.Threading.Tasks.ValueTask); } } diff --git a/TUnit.Core.SourceGenerator/CodeGenerators/Helpers/TupleArgumentHelper.cs b/TUnit.Core.SourceGenerator/CodeGenerators/Helpers/TupleArgumentHelper.cs index 53b3ecd87e..ef93c1ef6c 100644 --- a/TUnit.Core.SourceGenerator/CodeGenerators/Helpers/TupleArgumentHelper.cs +++ b/TUnit.Core.SourceGenerator/CodeGenerators/Helpers/TupleArgumentHelper.cs @@ -121,7 +121,10 @@ public static List GenerateArgumentAccessWithParams(IList {regularParamCount} ? global::System.Linq.Enumerable.Range({regularParamCount}, {rangeCount}).Select(i => global::TUnit.Core.Helpers.CastHelper.Cast<{elementType.GloballyQualified()}>({argumentsArrayName}[i])).ToArray() : new {elementType.GloballyQualified()}[0])"; + // Use fully-qualified static Enumerable.Select/ToArray calls rather than the + // extension-method chain — generated files don't import System.Linq unless the + // project has ImplicitUsings enabled, so the extension form fails to compile (CS1061). + var arrayInit = $"({argumentsArrayName}.Length > {regularParamCount} ? global::System.Linq.Enumerable.ToArray(global::System.Linq.Enumerable.Select(global::System.Linq.Enumerable.Range({regularParamCount}, {rangeCount}), i => global::TUnit.Core.Helpers.CastHelper.Cast<{elementType.GloballyQualified()}>({argumentsArrayName}[i]))) : new {elementType.GloballyQualified()}[0])"; argumentExpressions.Add(arrayInit); } else From e54f98db1c2efb6125318afcd3641b387ce314d1 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Mon, 1 Jun 2026 09:55:36 +0100 Subject: [PATCH 2/3] test(sourcegen): regression test for params array Linq compilation (#6140) Compiles the generated output WITHOUT `using System.Linq` in scope and asserts the resulting C# compilation has no errors. Fails with CS1061 against the pre-fix extension-method chain; passes with the fully-qualified static calls. The existing snapshot harness can't catch this class of bug: TestsBase injects `global using global::System.Linq;` and only inspects generator diagnostics, never the resulting compilation's C# errors. --- .../ParamsArrayCompilationTests.cs | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 TUnit.Core.SourceGenerator.Tests/ParamsArrayCompilationTests.cs diff --git a/TUnit.Core.SourceGenerator.Tests/ParamsArrayCompilationTests.cs b/TUnit.Core.SourceGenerator.Tests/ParamsArrayCompilationTests.cs new file mode 100644 index 0000000000..9c7385f017 --- /dev/null +++ b/TUnit.Core.SourceGenerator.Tests/ParamsArrayCompilationTests.cs @@ -0,0 +1,86 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using TUnit.Core.SourceGenerator.Generators; + +namespace TUnit.Core.SourceGenerator.Tests; + +/// +/// Regression test for https://github.com/thomhurst/TUnit/issues/6140. +/// +/// The params/array argument binding used to emit the LINQ extension-method chain +/// Enumerable.Range(...).Select(...).ToArray(). Generated .g.cs files only +/// import System.Linq when the consuming project has ImplicitUsings enabled, so +/// projects without it failed to compile with CS1061 ("IEnumerable<T> does not contain a +/// definition for 'Select'"). +/// +/// Unlike the snapshot tests (whose harness injects a global using System.Linq; and only +/// inspects generator diagnostics, never the resulting C# compilation), this test compiles the +/// generated output WITHOUT System.Linq in scope and asserts there are no compiler errors. +/// It fails (CS1061) against the pre-fix generator and passes once the generated code uses +/// fully-qualified static Enumerable.ToArray(Enumerable.Select(...)) calls. +/// +internal class ParamsArrayCompilationTests +{ + // A params test method whose body uses no LINQ — so the ONLY thing that could pull in + // System.Linq is the generated argument-binding code, isolating the regression. + private const string Source = + """ + global using global::System; + global using global::System.Collections.Generic; + global using global::System.Threading.Tasks; + global using global::TUnit.Core; + + namespace MyTests; + + public enum RollingOrder { NewestFirst, NewestLast } + + public class RollingListTests + { + [Test] + [Arguments(RollingOrder.NewestFirst, 2, 1)] + [Arguments(RollingOrder.NewestLast, 1, 2)] + public void Add(RollingOrder rollingOrder, params int[] expected) + { + } + + [Test] + [Arguments("a", "b", "c")] + public void LeadingParams(params string[] values) + { + } + } + """; + + [Test] + public async Task Generated_params_binding_compiles_without_System_Linq_in_scope() + { + // Generated code (and the project) targets the preview language version. + var parseOptions = new CSharpParseOptions(LanguageVersion.Preview); + + var compilation = CSharpCompilation.Create( + "ParamsArrayRegression", + [CSharpSyntaxTree.ParseText(Source, parseOptions)], + ReferencesHelper.References, + new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); + + GeneratorDriver driver = CSharpGeneratorDriver.Create( + [new TestMetadataGenerator().AsSourceGenerator()], + parseOptions: parseOptions); + driver.RunGeneratorsAndUpdateCompilation(compilation, out var output, out _); + + var errors = output.GetDiagnostics() + .Where(d => d.Severity == DiagnosticSeverity.Error) + .ToArray(); + + // Surface the full generated source if anything fails, so failures are diagnosable. + var generated = string.Join( + Environment.NewLine + "----" + Environment.NewLine, + output.SyntaxTrees.Select(t => t.GetText().ToString())); + + await Assert.That(errors) + .IsEmpty() + .Because($"generated code must compile without 'using System.Linq'. Errors:{Environment.NewLine}" + + string.Join(Environment.NewLine, errors.Select(e => e.ToString())) + + Environment.NewLine + generated); + } +} From 591b07d9e293ff89f2bdb070b7d01b0b9e140c6a Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Mon, 1 Jun 2026 10:06:53 +0100 Subject: [PATCH 3/3] docs(test): warn that injected global usings mask missing-using bugs (#6140) Addresses PR review feedback: document in TestsBase that the injected `global using System.Linq;` hides CS1061-style bugs from snapshot tests, so generated code must use fully-qualified static calls. --- TUnit.Core.SourceGenerator.Tests/TestsBase.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/TUnit.Core.SourceGenerator.Tests/TestsBase.cs b/TUnit.Core.SourceGenerator.Tests/TestsBase.cs index 3fbf1c12ec..949d519755 100644 --- a/TUnit.Core.SourceGenerator.Tests/TestsBase.cs +++ b/TUnit.Core.SourceGenerator.Tests/TestsBase.cs @@ -62,6 +62,11 @@ public async Task RunTest(string inputFile, RunTestOptions runTestOptions, Func< string[] additionalSources = [ + // WARNING: these injected global usings (notably System.Linq) mean snapshot tests will + // NOT catch CS1061-style "missing using" bugs in generated code — generated output is + // compiled here with these usings always in scope. Generated code must therefore use + // fully-qualified static calls (e.g. global::System.Linq.Enumerable.ToArray) rather than + // extension-method chains. See ParamsArrayCompilationTests for a compile check without them. """ // global using global::System;