From b77923bfff77e5d4c85a2a48d90174618aeb85ce Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 31 Mar 2026 01:37:29 +0000 Subject: [PATCH 1/3] Fix String.IndexOf/LastIndexOf with StringComparison argument Previously, calling `String.IndexOf("s", StringComparison.Ordinal)` in Python would either produce a compile error (three-arg form) or silently misinterpret the StringComparison enum value as a start-index (two-arg form, since the enum int value is passed to `find()`). Add pattern cases to filter out the StringComparison argument, matching the approach used in the JS/TS target. Also add regression tests. Co-Authored-By: Claude Sonnet 4.6 --- src/Fable.Cli/CHANGELOG.md | 1 + src/Fable.Compiler/CHANGELOG.md | 1 + src/Fable.Transforms/Python/Replacements.fs | 20 ++++++++++++++++++++ tests/Python/TestString.fs | 20 ++++++++++++++++++++ 4 files changed, 42 insertions(+) diff --git a/src/Fable.Cli/CHANGELOG.md b/src/Fable.Cli/CHANGELOG.md index bfdd64ed59..3cbfe9a6ed 100644 --- a/src/Fable.Cli/CHANGELOG.md +++ b/src/Fable.Cli/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +* [Python] Fix `String.IndexOf`/`LastIndexOf` with `StringComparison` argument emitting it as a start-index instead of a compile error (by @repo-assist) * [Dart] Enable explicit variable typing for more data types (by @ncave) * [Dart] Fix tests for `round`, `sign`, `truncate`, `log`, `log2`, `log10`, `pow`, `DivRem`, `Min`, `Max`, `Clamp`, `MinMagnitude`, `MaxMagnitude`, `cosh`, `sinh`, `tanh`, and float `Parse` (by @ncave) * [Python] Add `Math.DivRem` support for int, int64, and bigint (by @dbrattli) diff --git a/src/Fable.Compiler/CHANGELOG.md b/src/Fable.Compiler/CHANGELOG.md index 3f87467b3b..722fcd6ee7 100644 --- a/src/Fable.Compiler/CHANGELOG.md +++ b/src/Fable.Compiler/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +* [Python] Fix `String.IndexOf`/`LastIndexOf` with `StringComparison` argument emitting it as a start-index instead of a compile error (by @repo-assist) * [Dart] Enable explicit variable typing for more data types (by @ncave) * [Dart] Fix tests for `round`, `sign`, `truncate`, `log`, `log2`, `log10`, `pow`, `DivRem`, `Min`, `Max`, `Clamp`, `MinMagnitude`, `MaxMagnitude`, `cosh`, `sinh`, `tanh`, and float `Parse` (by @ncave) * [Python] Add `Math.DivRem` support for int, int64, and bigint (by @dbrattli) diff --git a/src/Fable.Transforms/Python/Replacements.fs b/src/Fable.Transforms/Python/Replacements.fs index ce8f64fb6d..a57b77fe52 100644 --- a/src/Fable.Transforms/Python/Replacements.fs +++ b/src/Fable.Transforms/Python/Replacements.fs @@ -1503,6 +1503,10 @@ let strings (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr opt | [ ExprType Char; ExprType(Number(Int32, NumberInfo.Empty)) ] | [ ExprType String; ExprType(Number(Int32, NumberInfo.Empty)) ] -> Helper.InstanceCall(c, "find", t, args, i.SignatureArgTypes, ?loc = r) |> Some + | [ ExprType String; StringComparisonEnumValue ] -> + Helper.InstanceCall(c, "find", t, [ List.head args ], i.SignatureArgTypes, ?loc = r) |> Some + | [ ExprType String; ExprType(Number(Int32, NumberInfo.Empty)); StringComparisonEnumValue ] -> + Helper.InstanceCall(c, "find", t, List.take 2 args, i.SignatureArgTypes, ?loc = r) |> Some | _ -> "The only extra argument accepted for String.IndexOf/LastIndexOf is startIndex." |> addErrorAndReturnNull com ctx.InlinePath r @@ -1526,6 +1530,22 @@ let strings (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr opt ?loc = r ) |> Some + | [ ExprType String; StringComparisonEnumValue ] -> + Helper.InstanceCall(c, "rfind", t, [ List.head args ], i.SignatureArgTypes, ?loc = r) |> Some + | [ ExprType String as str; ExprType(Number(Int32, NumberInfo.Empty)) as start; StringComparisonEnumValue ] -> + Helper.InstanceCall( + c, + "rfind", + t, + [ + str + Value(NumberConstant(NumberValue.Int32 0, NumberInfo.Empty), None) + start + ], + i.SignatureArgTypes, + ?loc = r + ) + |> Some | _ -> "The only extra argument accepted for String.IndexOf/LastIndexOf is startIndex." |> addErrorAndReturnNull com ctx.InlinePath r diff --git a/tests/Python/TestString.fs b/tests/Python/TestString.fs index e9f7dc4e36..c649490229 100644 --- a/tests/Python/TestString.fs +++ b/tests/Python/TestString.fs @@ -567,6 +567,26 @@ let ``test String.LastIndexOf works with offset`` () = "abcdbcebc".LastIndexOf("bc", 3) |> equal 1 +[] +let ``test String.IndexOf with StringComparison works`` () = + "abcdbc".IndexOf("b", StringComparison.Ordinal) + |> equal 1 + +[] +let ``test String.IndexOf with index and StringComparison works`` () = + "abcdbc".IndexOf("b", 3, StringComparison.Ordinal) + |> equal 4 + +[] +let ``test String.LastIndexOf with StringComparison works`` () = + "abcdbc".LastIndexOf("b", StringComparison.Ordinal) + |> equal 4 + +[] +let ``test String.LastIndexOf with index and StringComparison works`` () = + "abcdbc".LastIndexOf("b", 3, StringComparison.Ordinal) + |> equal 1 + [] let ``test String.IndexOfAny works`` () = From 198675b6bf4390cdbf63f34c8c3f02082c323b14 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Wed, 1 Apr 2026 06:51:38 +0200 Subject: [PATCH 2/3] format --- src/Fable.Transforms/Python/Replacements.fs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Fable.Transforms/Python/Replacements.fs b/src/Fable.Transforms/Python/Replacements.fs index a57b77fe52..01de018f38 100644 --- a/src/Fable.Transforms/Python/Replacements.fs +++ b/src/Fable.Transforms/Python/Replacements.fs @@ -1504,9 +1504,11 @@ let strings (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr opt | [ ExprType String; ExprType(Number(Int32, NumberInfo.Empty)) ] -> Helper.InstanceCall(c, "find", t, args, i.SignatureArgTypes, ?loc = r) |> Some | [ ExprType String; StringComparisonEnumValue ] -> - Helper.InstanceCall(c, "find", t, [ List.head args ], i.SignatureArgTypes, ?loc = r) |> Some + Helper.InstanceCall(c, "find", t, [ List.head args ], i.SignatureArgTypes, ?loc = r) + |> Some | [ ExprType String; ExprType(Number(Int32, NumberInfo.Empty)); StringComparisonEnumValue ] -> - Helper.InstanceCall(c, "find", t, List.take 2 args, i.SignatureArgTypes, ?loc = r) |> Some + Helper.InstanceCall(c, "find", t, List.take 2 args, i.SignatureArgTypes, ?loc = r) + |> Some | _ -> "The only extra argument accepted for String.IndexOf/LastIndexOf is startIndex." |> addErrorAndReturnNull com ctx.InlinePath r @@ -1531,7 +1533,8 @@ let strings (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr opt ) |> Some | [ ExprType String; StringComparisonEnumValue ] -> - Helper.InstanceCall(c, "rfind", t, [ List.head args ], i.SignatureArgTypes, ?loc = r) |> Some + Helper.InstanceCall(c, "rfind", t, [ List.head args ], i.SignatureArgTypes, ?loc = r) + |> Some | [ ExprType String as str; ExprType(Number(Int32, NumberInfo.Empty)) as start; StringComparisonEnumValue ] -> Helper.InstanceCall( c, From 05a62a2a4cf013066b8e11af53f91a5b1977af2f Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Thu, 2 Apr 2026 09:27:04 +0200 Subject: [PATCH 3/3] Refactor IndexOf/LastIndexOf to filter-based StringComparison handling Replace 4 duplicated match arms with List.filter to strip StringComparison args before pattern matching, matching the JS/TS target's approach. Add IndexOf char with StringComparison test. Co-Authored-By: Claude Sonnet 4.6 --- src/Fable.Transforms/Python/Replacements.fs | 39 +++++++++------------ tests/Python/TestString.fs | 5 +++ 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/src/Fable.Transforms/Python/Replacements.fs b/src/Fable.Transforms/Python/Replacements.fs index 01de018f38..311d173e62 100644 --- a/src/Fable.Transforms/Python/Replacements.fs +++ b/src/Fable.Transforms/Python/Replacements.fs @@ -1497,23 +1497,33 @@ let strings (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr opt Some c, args -> Helper.InstanceCall(c, methName, t, args, i.SignatureArgTypes, ?loc = r) |> Some | "IndexOf", Some c, _ -> + let args = + args + |> List.filter ( + function + | StringComparisonEnumValue -> false + | _ -> true + ) + match args with | [ ExprType Char ] | [ ExprType String ] | [ ExprType Char; ExprType(Number(Int32, NumberInfo.Empty)) ] | [ ExprType String; ExprType(Number(Int32, NumberInfo.Empty)) ] -> Helper.InstanceCall(c, "find", t, args, i.SignatureArgTypes, ?loc = r) |> Some - | [ ExprType String; StringComparisonEnumValue ] -> - Helper.InstanceCall(c, "find", t, [ List.head args ], i.SignatureArgTypes, ?loc = r) - |> Some - | [ ExprType String; ExprType(Number(Int32, NumberInfo.Empty)); StringComparisonEnumValue ] -> - Helper.InstanceCall(c, "find", t, List.take 2 args, i.SignatureArgTypes, ?loc = r) - |> Some | _ -> "The only extra argument accepted for String.IndexOf/LastIndexOf is startIndex." |> addErrorAndReturnNull com ctx.InlinePath r |> Some | "LastIndexOf", Some c, _ -> + let args = + args + |> List.filter ( + function + | StringComparisonEnumValue -> false + | _ -> true + ) + match args with | [ ExprType Char ] | [ ExprType String ] -> Helper.InstanceCall(c, "rfind", t, args, i.SignatureArgTypes, ?loc = r) |> Some @@ -1532,23 +1542,6 @@ let strings (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr opt ?loc = r ) |> Some - | [ ExprType String; StringComparisonEnumValue ] -> - Helper.InstanceCall(c, "rfind", t, [ List.head args ], i.SignatureArgTypes, ?loc = r) - |> Some - | [ ExprType String as str; ExprType(Number(Int32, NumberInfo.Empty)) as start; StringComparisonEnumValue ] -> - Helper.InstanceCall( - c, - "rfind", - t, - [ - str - Value(NumberConstant(NumberValue.Int32 0, NumberInfo.Empty), None) - start - ], - i.SignatureArgTypes, - ?loc = r - ) - |> Some | _ -> "The only extra argument accepted for String.IndexOf/LastIndexOf is startIndex." |> addErrorAndReturnNull com ctx.InlinePath r diff --git a/tests/Python/TestString.fs b/tests/Python/TestString.fs index c649490229..925ddd707e 100644 --- a/tests/Python/TestString.fs +++ b/tests/Python/TestString.fs @@ -572,6 +572,11 @@ let ``test String.IndexOf with StringComparison works`` () = "abcdbc".IndexOf("b", StringComparison.Ordinal) |> equal 1 +[] +let ``test String.IndexOf char with StringComparison works`` () = + "abcdbc".IndexOf('b', StringComparison.Ordinal) + |> equal 1 + [] let ``test String.IndexOf with index and StringComparison works`` () = "abcdbc".IndexOf("b", 3, StringComparison.Ordinal)