diff --git a/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs b/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs index 1cc27d6212..dad066f2bf 100644 --- a/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs +++ b/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs @@ -117,6 +117,9 @@ namespace System.Management.Automation new ExperimentalFeature( name: "PSNullConditionalOperators", description: "Support the null conditional member access operators in PowerShell language"), + new ExperimentalFeature( + name: "PSCultureInvariantReplaceOperator", + description: "Use culture invariant to-string convertor for lval in replace operator"), }; EngineExperimentalFeatures = new ReadOnlyCollection(engineFeatures); diff --git a/src/System.Management.Automation/engine/lang/parserutils.cs b/src/System.Management.Automation/engine/lang/parserutils.cs index e629a427b7..4d227ad7c9 100644 --- a/src/System.Management.Automation/engine/lang/parserutils.cs +++ b/src/System.Management.Automation/engine/lang/parserutils.cs @@ -967,7 +967,15 @@ namespace System.Management.Automation IEnumerator list = LanguagePrimitives.GetEnumerator(lval); if (list == null) { - string lvalString = lval?.ToString() ?? string.Empty; + string lvalString; + if (ExperimentalFeature.IsEnabled("PSCultureInvariantReplaceOperator")) + { + lvalString = PSObject.ToStringParser(context, lval) ?? string.Empty; + } + else + { + lvalString = lval?.ToString() ?? string.Empty; + } return ReplaceOperatorImpl(context, lvalString, rr, substitute); } diff --git a/test/powershell/Language/Operators/ReplaceOperator.Tests.ps1 b/test/powershell/Language/Operators/ReplaceOperator.Tests.ps1 index c620221d4d..a818725a87 100644 --- a/test/powershell/Language/Operators/ReplaceOperator.Tests.ps1 +++ b/test/powershell/Language/Operators/ReplaceOperator.Tests.ps1 @@ -64,4 +64,35 @@ Describe "Replace Operator" -Tags CI { $res | Should -BeExactly "ID XXXX123" } } + + Describe "Culture-invariance tests for -split and -replace" -Tags CI { + BeforeAll { + $skipTest = -not [ExperimentalFeature]::IsEnabled("PSCultureInvariantReplaceOperator") + if ($skipTest) { + Write-Verbose "Test Suite Skipped. The test suite requires the experimental feature 'PSCultureInvariantReplaceOperator' to be enabled." -Verbose + $originalDefaultParameterValues = $PSDefaultParameterValues.Clone() + $PSDefaultParameterValues["it:skip"] = $true + } else { + $prevCulture = [cultureinfo]::CurrentCulture + # The French culture uses "," as the decimal mark. + [cultureinfo]::CurrentCulture = 'fr' + } + } + + AfterAll { + if ($skipTest) { + $global:PSDefaultParameterValues = $originalDefaultParameterValues + } else { + [cultureinfo]::CurrentCulture = $prevCulture + } + } + + It "-split: LHS stringification is not culture-sensitive" { + 1.2 -split ',' | Should -Be '1.2' + } + + It "-replace: LHS stringification is not culture-sensitive" { + 1.2 -replace ',' | Should -Be '1.2' + } + } } diff --git a/test/tools/TestMetadata.json b/test/tools/TestMetadata.json index c49ab1758c..f069577eb2 100644 --- a/test/tools/TestMetadata.json +++ b/test/tools/TestMetadata.json @@ -1,5 +1,6 @@ { "ExperimentalFeatures": { - "ExpTest.FeatureOne": [ "test/powershell/engine/ExperimentalFeature/ExperimentalFeature.Basic.Tests.ps1" ] + "ExpTest.FeatureOne": [ "test/powershell/engine/ExperimentalFeature/ExperimentalFeature.Basic.Tests.ps1" ], + "PSCultureInvariantReplaceOperator": [ "test/powershell/Language/Operators/ReplaceOperator.Tests.ps1" ] } }