From d217073714d958cced002dd6b93b2adce062cb34 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Tue, 4 Mar 2025 10:56:18 -0500 Subject: [PATCH] Use invariant culture when formatting transfer capture in regex source generator (#113081) A balancing group can result in TransferCapture being emitted with a negative "capnum". If the compiler is running under a culture that uses something other than '-' as the negative sign, the resulting generated code will fail to compile. --- .../gen/RegexGenerator.Emitter.cs | 2 +- .../RegexGeneratorHelper.netcoreapp.cs | 29 +++++++++++++++---- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.Emitter.cs b/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.Emitter.cs index a64b43d6884737..54db918d6bae7a 100644 --- a/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.Emitter.cs +++ b/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.Emitter.cs @@ -2549,7 +2549,7 @@ void EmitCapture(RegexNode node, RegexNode? subsequent = null) } else { - writer.WriteLine($"base.TransferCapture({capnum}, {uncapnum}, {startingPos}, pos);"); + writer.WriteLine($"base.TransferCapture({capnum.ToString(CultureInfo.InvariantCulture)}, {uncapnum}, {startingPos}, pos);"); } if (isAtomic || !childBacktracks) diff --git a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/RegexGeneratorHelper.netcoreapp.cs b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/RegexGeneratorHelper.netcoreapp.cs index fe0e793819f646..90e638570f629f 100644 --- a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/RegexGeneratorHelper.netcoreapp.cs +++ b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/RegexGeneratorHelper.netcoreapp.cs @@ -134,6 +134,12 @@ internal static async Task SourceGenRegexAsync( return results[0]; } + private static readonly CultureInfo s_cultureWithMinusNegativeSign = new CultureInfo("") + { + // To validate that generation still succeeds even when something other than '-' is used. + NumberFormat = new NumberFormatInfo() { NegativeSign = $"{(char)0x2212}" } + }; + internal static async Task SourceGenRegexAsync( (string pattern, CultureInfo? culture, RegexOptions? options, TimeSpan? matchTimeout)[] regexes, CancellationToken cancellationToken = default) { @@ -214,13 +220,24 @@ internal static async Task SourceGenRegexAsync( comp = comp.ReplaceSyntaxTree(comp.SyntaxTrees.First(), CSharpSyntaxTree.ParseText(SourceText.From(code.ToString(), Encoding.UTF8), s_previewParseOptions)); // Run the generator - GeneratorDriverRunResult generatorResults = s_generatorDriver.RunGenerators(comp!, cancellationToken).GetRunResult(); - ImmutableArray generatorDiagnostics = generatorResults.Diagnostics.RemoveAll(d => d.Severity <= DiagnosticSeverity.Hidden); - if (generatorDiagnostics.Length != 0) + CultureInfo origCulture = CultureInfo.CurrentCulture; + CultureInfo.CurrentCulture = s_cultureWithMinusNegativeSign; + GeneratorDriverRunResult generatorResults; + ImmutableArray generatorDiagnostics; + try { - throw new ArgumentException( - string.Join(Environment.NewLine, generatorResults.GeneratedTrees.Select(t => NumberLines(t.ToString()))) + Environment.NewLine + - string.Join(Environment.NewLine, generatorDiagnostics)); + generatorResults = s_generatorDriver.RunGenerators(comp!, cancellationToken).GetRunResult(); + generatorDiagnostics = generatorResults.Diagnostics.RemoveAll(d => d.Severity <= DiagnosticSeverity.Hidden); + if (generatorDiagnostics.Length != 0) + { + throw new ArgumentException( + string.Join(Environment.NewLine, generatorResults.GeneratedTrees.Select(t => NumberLines(t.ToString()))) + Environment.NewLine + + string.Join(Environment.NewLine, generatorDiagnostics)); + } + } + finally + { + CultureInfo.CurrentCulture = origCulture; } // Compile the assembly to a stream