Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/Fable.Cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

### Fixed

* [All] Replace unsafe option `.Value` unwrapping with safe alternatives in Python/Replacements.fs and Rust/Fable2Rust.fs (code scanning alerts IONIDE-006)
* [All] Add `[<return: Struct>]` to partial active patterns in Dart and Rust targets to reduce allocations (code scanning alerts IONIDE-009)

## 5.0.0-rc.6 - 2026-03-31

### Fixed
Expand Down
5 changes: 5 additions & 0 deletions src/Fable.Compiler/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

### Fixed

* [All] Replace unsafe option `.Value` unwrapping with safe alternatives in Python/Replacements.fs and Rust/Fable2Rust.fs (code scanning alerts IONIDE-006)
* [All] Add `[<return: Struct>]` to partial active patterns in Dart and Rust targets to reduce allocations (code scanning alerts IONIDE-009)

## 5.0.0-rc.12 - 2026-03-31

### Fixed
Expand Down
14 changes: 8 additions & 6 deletions src/Fable.Transforms/Dart/Fable2Dart.fs
Original file line number Diff line number Diff line change
Expand Up @@ -89,17 +89,19 @@ module Util =

let (|TransformType|) (com: IDartCompiler) ctx e = com.TransformType(ctx, e)

[<return: Struct>]
let (|Function|_|) =
function
| Fable.Lambda(arg, body, _) -> Some([ arg ], body)
| Fable.Delegate(args, body, _, []) -> Some(args, body)
| _ -> None
| Fable.Lambda(arg, body, _) -> ValueSome([ arg ], body)
| Fable.Delegate(args, body, _, []) -> ValueSome(args, body)
| _ -> ValueNone

[<return: Struct>]
let (|Lets|_|) =
function
| Fable.Let(ident, value, body) -> Some([ ident, value ], body)
| Fable.LetRec(bindings, body) -> Some(bindings, body)
| _ -> None
| Fable.Let(ident, value, body) -> ValueSome([ ident, value ], body)
| Fable.LetRec(bindings, body) -> ValueSome(bindings, body)
| _ -> ValueNone

let makeTypeRefFromName typeName genArgs =
let ident = makeImmutableIdent MetaType typeName
Expand Down
16 changes: 10 additions & 6 deletions src/Fable.Transforms/Dart/Replacements.fs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ open Fable.AST.Fable
open Fable.Transforms
open Replacements.Util

[<return: Struct>]
let (|DartInt|_|) =
function
| Int8
Expand All @@ -19,14 +20,15 @@ let (|DartInt|_|) =
| Int32
| UInt32
| Int64
| UInt64 -> Some DartInt
| _ -> None
| UInt64 -> ValueSome DartInt
| _ -> ValueNone

[<return: Struct>]
let (|DartDouble|_|) =
function
| Float32
| Float64 -> Some DartDouble
| _ -> None
| Float64 -> ValueSome DartDouble
| _ -> ValueNone

let error com msg =
let e = makeImportLib com Any "ExceptionBase" "Types"
Expand Down Expand Up @@ -1201,10 +1203,12 @@ let operators (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr o
let meth = Naming.lowerFirst meth

match meth, t with
| ("min" | "max"), Number((DartInt | DartDouble), NumberInfo.Empty) ->
| ("min" | "max"), Number(DartInt, NumberInfo.Empty)
| ("min" | "max"), Number(DartDouble, NumberInfo.Empty) ->
Helper.ImportedCall("dart:math", meth, t, args, i.SignatureArgTypes, ?loc = r)
|> Some
| ("minMagnitude" | "maxMagnitude"), Number((DartInt | DartDouble), NumberInfo.Empty) ->
| ("minMagnitude" | "maxMagnitude"), Number(DartInt, NumberInfo.Empty)
| ("minMagnitude" | "maxMagnitude"), Number(DartDouble, NumberInfo.Empty) ->
Helper.LibCall(com, "Util", meth, t, args, i.SignatureArgTypes, ?loc = r)
|> Some
| _ ->
Expand Down
2 changes: 1 addition & 1 deletion src/Fable.Transforms/Python/Replacements.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3229,7 +3229,7 @@ let regex com (ctx: Context) r t (i: CallInfo) (thisArg: Expr option) (args: Exp
| "get_Item" when i.DeclaringEntityFullName = Types.regexGroupCollection ->
Helper.LibCall(com, "RegExp", "get_item", t, [ thisArg.Value; args.Head ], [ thisArg.Value.Type ], ?loc = r)
|> Some
| "get_Item" -> getExpr r t thisArg.Value args.Head |> Some
| "get_Item" -> thisArg |> Option.map (fun this -> getExpr r t this args.Head)
| "get_Count" ->
// Use int32(len()) to ensure consistent return type
thisArg
Expand Down
67 changes: 39 additions & 28 deletions src/Fable.Transforms/Rust/Fable2Rust.fs
Original file line number Diff line number Diff line change
Expand Up @@ -897,6 +897,7 @@ module TypeInfo =
// most abstract classes are implemented as non-abstract
makeFullNamePathTy entName genArgsOpt

[<return: Struct>]
let (|HasEmitAttribute|_|) (ent: Fable.Entity) =
ent.Attributes
|> Seq.tryPick (fun att ->
Expand All @@ -907,13 +908,15 @@ module TypeInfo =
else
None
)
|> Option.toValueOption

type PointerType =
| Lrc
| Rc
| Arc
| Box

[<return: Struct>]
let (|HasReferenceTypeAttribute|_|) (ent: Fable.Entity) =
ent.Attributes
|> Seq.tryPick (fun att ->
Expand All @@ -930,17 +933,19 @@ module TypeInfo =
else
None
)
|> Option.toValueOption

[<return: Struct>]
let (|IsNonErasedInterface|_|) (com: Compiler) =
function
| Fable.DeclaredType(entRef, genArgs) ->
let ent = com.GetEntity(entRef)

if ent.IsInterface && not (ent |> FSharp2Fable.Util.hasAttribute Atts.erase) then
Some(entRef, genArgs)
ValueSome(entRef, genArgs)
else
None
| _ -> None
ValueNone
| _ -> ValueNone

let transformEntityType (com: IRustCompiler) ctx (entRef: Fable.EntityRef) genArgs : Rust.Ty =
match com.GetEntity(entRef) with
Expand Down Expand Up @@ -1176,43 +1181,50 @@ module Util =

let (|TransformExpr|) (com: IRustCompiler) ctx e = com.TransformExpr(ctx, e)

[<return: Struct>]
let (|Function|_|) =
function
| Fable.Lambda(arg, body, info) -> Some([ arg ], body, info)
| Fable.Delegate(args, body, info, []) -> Some(args, body, info)
| _ -> None
| Fable.Lambda(arg, body, info) -> ValueSome([ arg ], body, info)
| Fable.Delegate(args, body, info, []) -> ValueSome(args, body, info)
| _ -> ValueNone

[<return: Struct>]
let (|Lets|_|) =
function
| Fable.Let(ident, value, body) -> Some([ ident, value ], body)
| Fable.LetRec(bindings, body) -> Some(bindings, body)
| _ -> None
| Fable.Let(ident, value, body) -> ValueSome([ ident, value ], body)
| Fable.LetRec(bindings, body) -> ValueSome(bindings, body)
| _ -> ValueNone

[<return: Struct>]
let (|IDisposable|_|) =
function
| Replacements.Util.IsEntity (Types.idisposable) _ -> Some()
| _ -> None
| Replacements.Util.IsEntity (Types.idisposable) _ -> ValueSome()
| _ -> ValueNone

[<return: Struct>]
let (|IFormattable|_|) =
function
| Replacements.Util.IsEntity (Types.iformattable) _ -> Some()
| _ -> None
| Replacements.Util.IsEntity (Types.iformattable) _ -> ValueSome()
| _ -> ValueNone

[<return: Struct>]
let (|IComparable|_|) =
function
| Replacements.Util.IsEntity (Types.icomparableGeneric) (_, [ genArg ]) -> Some(genArg)
| _ -> None
| Replacements.Util.IsEntity (Types.icomparableGeneric) (_, [ genArg ]) -> ValueSome(genArg)
| _ -> ValueNone

[<return: Struct>]
let (|IEquatable|_|) =
function
| Replacements.Util.IsEntity (Types.iequatableGeneric) (_, [ genArg ]) -> Some(genArg)
| _ -> None
| Replacements.Util.IsEntity (Types.iequatableGeneric) (_, [ genArg ]) -> ValueSome(genArg)
| _ -> ValueNone

[<return: Struct>]
let (|IEnumerable|_|) =
function
| Replacements.Util.IsEntity (Types.ienumerableGeneric) (_, [ genArg ]) -> Some(genArg)
| Replacements.Util.IsEntity (Types.ienumerable) _ -> Some(Fable.Any)
| _ -> None
| Replacements.Util.IsEntity (Types.ienumerableGeneric) (_, [ genArg ]) -> ValueSome(genArg)
| Replacements.Util.IsEntity (Types.ienumerable) _ -> ValueSome(Fable.Any)
| _ -> ValueNone

let isUnitArg (ident: Fable.Ident) =
ident.IsCompilerGenerated
Expand Down Expand Up @@ -3779,10 +3791,9 @@ module Util =
let getFunctionBodyCtx com ctx (name: string option) (args: Fable.Ident list) (body: Fable.Expr) isTailRec =

let tco =
if isTailRec then
Some(NamedTailCallOpportunity(com, ctx, name.Value, args) :> ITailCallOpportunity)
else
None
match isTailRec, name with
| true, Some n -> Some(NamedTailCallOpportunity(com, ctx, n, args) :> ITailCallOpportunity)
| _ -> None

let usages = calcIdentUsages args [ body ]

Expand Down Expand Up @@ -3867,17 +3878,17 @@ module Util =
let fnBody = transformFunctionBody com ctx args body

let fnBody =
if isRecursive && not isTailRec then
match isRecursive && not isTailRec, name with
| true, Some n ->
// make the closure recursive with fixed-point combinator
let fixedArgs = (makeIdent name.Value) :: args
let fixedArgs = (makeIdent n) :: args
let fixedDecl = transformFunctionDecl com ctx fixedArgs [] Fable.Unit
let fixedBody = mkClosureExpr true fixedDecl fnBody
let argExprs = args |> List.map Fable.IdentExpr
let callArgs = transformCallArgs com ctx argExprs [] []
let fixCallArgs = (fixedBody |> mkAddrOfExpr) :: callArgs
makeLibCall com ctx None "Native" ("fix" + argCount) fixCallArgs
else
fnBody
| _ -> fnBody

let cloneStmts =
// clone captured idents (in 'move' closures)
Expand Down
Loading