Skip to content

Commit 3ce1e50

Browse files
authored
Fix preferreduilang switch leaking into fsi.CommandLineArgs (#19151)
1 parent aa5b719 commit 3ce1e50

File tree

4 files changed

+183
-1
lines changed

4 files changed

+183
-1
lines changed

docs/release-notes/.FSharp.Compiler.Service/10.0.200.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
### Fixed
22

33
* Type relations cache: optimize key generation ([Issue #19116](https://github.com/dotnet/fsharp/issues/18767)) ([PR #19120](https://github.com/dotnet/fsharp/pull/19120))
4+
* Fix `--preferreduilang` switch leaking into `fsi.CommandLineArgs` when positioned after script file ([PR #19151](https://github.com/dotnet/fsharp/pull/19151))
45

56
### Added
67

src/Compiler/Interactive/fsi.fs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1068,8 +1068,41 @@ type internal FsiCommandLineOptions(fsi: FsiEvaluationSessionHostConfig, argv: s
10681068
(fun args ->
10691069
let scriptFile = args[0]
10701070
let scriptArgs = List.tail args
1071+
1072+
// Filter out and process preferreduilang from script args
1073+
let isPreferredUiLangArg (arg: string) =
1074+
arg.StartsWith("--preferreduilang:", StringComparison.OrdinalIgnoreCase)
1075+
|| arg.StartsWith("/preferreduilang:", StringComparison.OrdinalIgnoreCase)
1076+
1077+
let rec filterScriptArgs (args: string list) =
1078+
match args with
1079+
| [] -> []
1080+
| (arg: string) :: rest when isPreferredUiLangArg arg ->
1081+
// Extract culture and set it
1082+
let colonIndex = arg.IndexOf(':')
1083+
1084+
if colonIndex >= 0 && colonIndex < arg.Length - 1 then
1085+
let culture = arg.Substring(colonIndex + 1)
1086+
1087+
try
1088+
// Validate culture first by creating CultureInfo
1089+
let cultureInfo = CultureInfo(culture)
1090+
// Only set if valid
1091+
tcConfigB.preferredUiLang <- Some culture
1092+
Thread.CurrentThread.CurrentUICulture <- cultureInfo
1093+
with
1094+
| :? CultureNotFoundException
1095+
| :? ArgumentException ->
1096+
// Ignore invalid culture, just don't set it
1097+
()
1098+
1099+
filterScriptArgs rest
1100+
| arg :: rest -> arg :: filterScriptArgs rest
1101+
1102+
let filteredScriptArgs = filterScriptArgs scriptArgs
1103+
10711104
inputFilesAcc <- inputFilesAcc @ [ (scriptFile, true) ] (* record script.fsx for evaluation *)
1072-
List.iter recordExplicitArg scriptArgs (* record rest of line as explicit arguments *)
1105+
List.iter recordExplicitArg filteredScriptArgs (* record rest of line as explicit arguments *)
10731106
tcConfigB.noFeedback <- true (* "quiet", no banners responses etc *)
10741107
interact <- false (* --exec, exit after eval *)
10751108
[] (* no arguments passed on, all consumed here *)

tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@
278278
<Compile Include="Interop\Literals.fs" />
279279
<Compile Include="Scripting\Interactive.fs" />
280280
<Compile Include="Scripting\TypeCheckOnlyTests.fs" />
281+
<Compile Include="Scripting\PreferredUiLangTests.fs" />
281282
<Compile Include="TypeChecks\TypeRelations.fs" />
282283
<Compile Include="TypeChecks\SeqTypeCheckTests.fs" />
283284
<Compile Include="TypeChecks\CheckDeclarationsTests.fs" />
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
2+
3+
namespace Scripting
4+
5+
open Xunit
6+
open System
7+
open System.IO
8+
open FSharp.Test
9+
open FSharp.Test.CompilerAssertHelpers
10+
open FSharp.Compiler.Interactive.Shell
11+
open FSharp.Compiler.Diagnostics
12+
13+
module ``PreferredUiLang tests`` =
14+
15+
[<Fact>]
16+
let ``preferreduilang switch before script is consumed from CommandLineArgs``() =
17+
let scriptContent = """
18+
printfn "Args: %A" fsi.CommandLineArgs
19+
if fsi.CommandLineArgs.Length <> 2 then exit 1
20+
if fsi.CommandLineArgs.[0] <> __SOURCE_FILE__ then exit 1
21+
if fsi.CommandLineArgs.[1] <> "arg1" then exit 1
22+
exit 0
23+
"""
24+
let tmpFile = Path.GetTempFileName() + ".fsx"
25+
try
26+
File.WriteAllText(tmpFile, scriptContent)
27+
let errors, _, _ =
28+
CompilerAssert.RunScriptWithOptionsAndReturnResult
29+
[| "--preferreduilang:es-ES"; tmpFile; "arg1" |]
30+
""
31+
32+
// Should succeed (exit 0)
33+
Assert.True((errors: ResizeArray<string>).Count = 0, sprintf "Expected no errors, got: %A" errors)
34+
finally
35+
if File.Exists(tmpFile) then File.Delete(tmpFile)
36+
37+
[<Fact>]
38+
let ``preferreduilang switch after script is consumed from CommandLineArgs``() =
39+
let scriptContent = """
40+
printfn "Args: %A" fsi.CommandLineArgs
41+
if fsi.CommandLineArgs.Length <> 2 then exit 1
42+
if fsi.CommandLineArgs.[0] <> __SOURCE_FILE__ then exit 1
43+
if fsi.CommandLineArgs.[1] <> "arg1" then exit 1
44+
exit 0
45+
"""
46+
let tmpFile = Path.GetTempFileName() + ".fsx"
47+
try
48+
File.WriteAllText(tmpFile, scriptContent)
49+
let errors, _, _ =
50+
CompilerAssert.RunScriptWithOptionsAndReturnResult
51+
[| tmpFile; "--preferreduilang:es-ES"; "arg1" |]
52+
""
53+
54+
// Should succeed (exit 0)
55+
Assert.True((errors: ResizeArray<string>).Count = 0, sprintf "Expected no errors, got: %A" errors)
56+
finally
57+
if File.Exists(tmpFile) then File.Delete(tmpFile)
58+
59+
[<Fact>]
60+
let ``preferreduilang with slash form is consumed from CommandLineArgs``() =
61+
let scriptContent = """
62+
printfn "Args: %A" fsi.CommandLineArgs
63+
if fsi.CommandLineArgs.Length <> 2 then exit 1
64+
if fsi.CommandLineArgs.[0] <> __SOURCE_FILE__ then exit 1
65+
if fsi.CommandLineArgs.[1] <> "arg1" then exit 1
66+
exit 0
67+
"""
68+
let tmpFile = Path.GetTempFileName() + ".fsx"
69+
try
70+
File.WriteAllText(tmpFile, scriptContent)
71+
let errors, _, _ =
72+
CompilerAssert.RunScriptWithOptionsAndReturnResult
73+
[| tmpFile; "/preferreduilang:de-DE"; "arg1" |]
74+
""
75+
76+
// Should succeed (exit 0)
77+
Assert.True((errors: ResizeArray<string>).Count = 0, sprintf "Expected no errors, got: %A" errors)
78+
finally
79+
if File.Exists(tmpFile) then File.Delete(tmpFile)
80+
81+
[<Fact>]
82+
let ``preferreduilang sets CurrentUICulture correctly``() =
83+
let scriptContent = """
84+
let culture = System.Threading.Thread.CurrentThread.CurrentUICulture.Name
85+
printfn "Culture: %s" culture
86+
if not (culture.StartsWith("fr-FR")) then
87+
printfn "Expected culture starting with fr-FR, got: %s" culture
88+
exit 1
89+
exit 0
90+
"""
91+
let tmpFile = Path.GetTempFileName() + ".fsx"
92+
try
93+
File.WriteAllText(tmpFile, scriptContent)
94+
let errors, _, _ =
95+
CompilerAssert.RunScriptWithOptionsAndReturnResult
96+
[| "--preferreduilang:fr-FR"; tmpFile |]
97+
""
98+
99+
// Should succeed (exit 0)
100+
Assert.True((errors: ResizeArray<string>).Count = 0, sprintf "Expected no errors, got: %A" errors)
101+
finally
102+
if File.Exists(tmpFile) then File.Delete(tmpFile)
103+
104+
[<Fact>]
105+
let ``preferreduilang after script also sets culture``() =
106+
let scriptContent = """
107+
let culture = System.Threading.Thread.CurrentThread.CurrentUICulture.Name
108+
printfn "Culture: %s" culture
109+
if not (culture.StartsWith("ja-JP")) then
110+
printfn "Expected culture starting with ja-JP, got: %s" culture
111+
exit 1
112+
exit 0
113+
"""
114+
let tmpFile = Path.GetTempFileName() + ".fsx"
115+
try
116+
File.WriteAllText(tmpFile, scriptContent)
117+
let errors, _, _ =
118+
CompilerAssert.RunScriptWithOptionsAndReturnResult
119+
[| tmpFile; "--preferreduilang:ja-JP" |]
120+
""
121+
122+
// Should succeed (exit 0)
123+
Assert.True((errors: ResizeArray<string>).Count = 0, sprintf "Expected no errors, got: %A" errors)
124+
finally
125+
if File.Exists(tmpFile) then File.Delete(tmpFile)
126+
127+
[<Fact>]
128+
let ``invalid culture in preferreduilang is ignored gracefully``() =
129+
let scriptContent = """
130+
printfn "Args: %A" fsi.CommandLineArgs
131+
if fsi.CommandLineArgs.Length <> 2 then exit 1
132+
if fsi.CommandLineArgs.[0] <> __SOURCE_FILE__ then exit 1
133+
if fsi.CommandLineArgs.[1] <> "arg1" then exit 1
134+
exit 0
135+
"""
136+
let tmpFile = Path.GetTempFileName() + ".fsx"
137+
try
138+
File.WriteAllText(tmpFile, scriptContent)
139+
let errors, _, _ =
140+
CompilerAssert.RunScriptWithOptionsAndReturnResult
141+
[| tmpFile; "--preferreduilang:invalid-culture-xyz"; "arg1" |]
142+
""
143+
144+
// Should succeed - invalid culture is ignored, but switch is still consumed
145+
Assert.True((errors: ResizeArray<string>).Count = 0, sprintf "Expected no errors, got: %A" errors)
146+
finally
147+
if File.Exists(tmpFile) then File.Delete(tmpFile)

0 commit comments

Comments
 (0)