@@ -933,3 +933,82 @@ module Expression =
933933 |> List.map f
934934 |> List.unzip
935935 |> fun ( results , stmtLists ) -> results, combine stmtLists
936+
937+ /// Utilities for extracting exception handler patterns from F# AST.
938+ /// F# compiles try-with expressions differently depending on the number of patterns:
939+ /// - 1-2 patterns: nested IfThenElse with TypeTest
940+ /// - 3+ patterns: DecisionTree with targets
941+ module ExceptionHandling =
942+
943+ /// Check if an expression is a reraise of the caught exception.
944+ /// Note: F# may rename the catch variable internally, so we check for any Throw(IdentExpr).
945+ let isReraise =
946+ function
947+ | Fable.Extended( Fable.Throw( Some( Fable.IdentExpr _), _), _) -> true
948+ | _ -> false
949+
950+ /// Extract exception handlers from a catch body that uses type tests.
951+ /// Returns: (handlers as (type * body) list, fallbackExpr option)
952+ ///
953+ /// Handles three patterns:
954+ /// - Simple: `| :? ExceptionType -> body`
955+ /// - Binding: `| :? ExceptionType as ex -> body`
956+ /// - DecisionTree: F# compiles 3+ exception patterns as a decision tree
957+ let rec extractExceptionHandlers ( expr : Fable.Expr ) : ( Fable.Type * Fable.Expr ) list * Fable.Expr option =
958+ match expr with
959+ // Binding pattern: | :? ExceptionType as ex -> body (check first as it's more specific)
960+ // F# compiles this as: IfThenElse(Test(param, TypeTest), Let(ex, TypeCast(param, typ), body), else)
961+ | Fable.IfThenElse( Fable.Test( Fable.IdentExpr _, Fable.TypeTest typ, _),
962+ ( Fable.Let(_, Fable.TypeCast( Fable.IdentExpr _, _), _) as thenExpr),
963+ elseExpr,
964+ _) ->
965+ let restHandlers , fallback = extractExceptionHandlers elseExpr
966+ ( typ, thenExpr) :: restHandlers, fallback
967+
968+ // Simple pattern: | :? ExceptionType -> body
969+ | Fable.IfThenElse( Fable.Test( Fable.IdentExpr _, Fable.TypeTest typ, _), thenExpr, elseExpr, _) ->
970+ let restHandlers , fallback = extractExceptionHandlers elseExpr
971+ ( typ, thenExpr) :: restHandlers, fallback
972+
973+ // DecisionTree pattern: F# compiles 3+ exception patterns as a decision tree
974+ | Fable.DecisionTree( decisionExpr, targets) ->
975+ // Extract type -> targetIndex mappings from the decision expression
976+ let rec extractFromDecision expr =
977+ match expr with
978+ | Fable.IfThenElse( Fable.Test( Fable.IdentExpr _, Fable.TypeTest typ, _),
979+ Fable.DecisionTreeSuccess( targetIdx, boundValues, _),
980+ elseExpr,
981+ _) -> ( typ, targetIdx, boundValues) :: extractFromDecision elseExpr
982+ | Fable.DecisionTreeSuccess( targetIdx, boundValues, _) ->
983+ // Wildcard/default case
984+ [ ( Fable.Any, targetIdx, boundValues) ]
985+ | _ -> []
986+
987+ let typeToTarget = extractFromDecision decisionExpr
988+
989+ // Map each type test to its handler body from targets
990+ // If there are bound values (from `as ex` pattern), wrap the body in Let bindings
991+ let handlers =
992+ typeToTarget
993+ |> List.choose ( fun ( typ , targetIdx , boundValues ) ->
994+ if targetIdx < List.length targets then
995+ let ( idents , body ) = targets.[ targetIdx]
996+ // Wrap body with Let bindings for each bound value
997+ let wrappedBody =
998+ List.zip idents boundValues
999+ |> List.fold ( fun acc ( ident , value ) -> Fable.Let( ident, value, acc)) body
1000+
1001+ Some( typ, wrappedBody)
1002+ else
1003+ None
1004+ )
1005+
1006+ // Separate the wildcard (Any) from specific type handlers
1007+ let specificHandlers = handlers |> List.filter ( fun ( typ , _ ) -> typ <> Fable.Any)
1008+
1009+ let wildcardHandler =
1010+ handlers |> List.tryFind ( fun ( typ , _ ) -> typ = Fable.Any) |> Option.map snd
1011+
1012+ specificHandlers, wildcardHandler
1013+
1014+ | _ -> [], Some expr
0 commit comments