Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

- Reanalyze: add glob pattern support for suppress/unsuppress configurations (e.g., `"src/generated/**"`). https://github.com/rescript-lang/rescript/pull/8277
- Add optional `~locales` and `~options` parameters to `String.localeCompare`. https://github.com/rescript-lang/rescript/pull/8287
- Add support for pattern matching/destructuring of record rest. https://github.com/rescript-lang/rescript/pull/8317

#### :bug: Bug fix

Expand Down
2 changes: 1 addition & 1 deletion analysis/reanalyze/src/DeadValue.ml
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ let collectPattern ~config ~refs :
fun super self pat ->
let posFrom = pat.Typedtree.pat_loc.loc_start in
(match pat.pat_desc with
| Typedtree.Tpat_record (cases, _clodsedFlag) ->
| Typedtree.Tpat_record (cases, _clodsedFlag, _rest) ->
cases
|> List.iter (fun (_loc, {Types.lbl_loc = {loc_start = posTo}}, _pat, _) ->
if !Config.analyzeTypes then
Expand Down
2 changes: 1 addition & 1 deletion analysis/src/CompletionFrontEnd.ml
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
(NPolyvariantPayload {itemNum = 0; constructorName = txt}
:: patternPath)
?contextPath p
| Ppat_record (fields, _) ->
| Ppat_record (fields, _, _rest) ->
Ext_list.iter fields (fun {lid = fname; x = p} ->
match fname with
| {Location.txt = Longident.Lident fname} ->
Expand Down
4 changes: 2 additions & 2 deletions analysis/src/CompletionPatterns.ml
Original file line number Diff line number Diff line change
Expand Up @@ -102,12 +102,12 @@ and traversePattern (pat : Parsetree.pattern) ~patternPath ~locHasCursor
[Completable.NTupleItem {itemNum}] @ patternPath)
~resultFromFoundItemNum:(fun itemNum ->
[Completable.NTupleItem {itemNum = itemNum + 1}] @ patternPath)
| Ppat_record ([], _) ->
| Ppat_record ([], _, _rest) ->
(* Empty fields means we're in a record body `{}`. Complete for the fields. *)
someIfHasCursor
("", [Completable.NRecordBody {seenFields = []}] @ patternPath)
"Ppat_record(empty)"
| Ppat_record (fields, _) -> (
| Ppat_record (fields, _, _rest) -> (
let fieldWithCursor = ref None in
let fieldWithPatHole = ref None in
Ext_list.iter fields (fun {lid = fname; x = f} ->
Expand Down
2 changes: 1 addition & 1 deletion analysis/src/DumpAst.ml
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ let rec printPattern pattern ~pos ~indentation =
| None -> ""
| Some pat -> "," ^ printPattern pat ~pos ~indentation)
^ ")"
| Ppat_record (fields, _) ->
| Ppat_record (fields, _, _rest) ->
"Ppat_record(\n"
^ addIndentation (indentation + 1)
^ "fields:\n"
Expand Down
2 changes: 1 addition & 1 deletion analysis/src/Hint.ml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ let inlay ~path ~pos ~maxLength ~debug =
let rec processPattern (pat : Parsetree.pattern) =
match pat.ppat_desc with
| Ppat_tuple pl -> pl |> List.iter processPattern
| Ppat_record (fields, _) ->
| Ppat_record (fields, _, _rest) ->
Ext_list.iter fields (fun {x = p} -> processPattern p)
| Ppat_array fields -> fields |> List.iter processPattern
| Ppat_var {loc} -> push loc Type
Expand Down
23 changes: 21 additions & 2 deletions analysis/src/ProcessCmt.ml
Original file line number Diff line number Diff line change
Expand Up @@ -512,8 +512,27 @@ let rec forStructureItem ~(env : SharedTypes.Env.t) ~(exported : Exported.t)
| Tpat_tuple pats | Tpat_array pats | Tpat_construct (_, _, pats) ->
pats |> List.iter (fun p -> handlePattern [] p)
| Tpat_or (p, _, _) -> handlePattern [] p
| Tpat_record (items, _) ->
items |> List.iter (fun (_, _, p, _) -> handlePattern [] p)
| Tpat_record (record_items, _, rest) -> (
record_items |> List.iter (fun (_, _, p, _) -> handlePattern [] p);
match rest with
| None -> ()
| Some rest ->
let declared =
addDeclared ~name:rest.rest_name
~stamp:(Ident.binding_time rest.rest_ident)
~env ~extent:rest.rest_name.loc ~item:rest.rest_type []
(Exported.add exported Exported.Value)
Stamps.addValue
in
items :=
{
Module.kind = Module.Value declared.item;
name = declared.name.txt;
docstring = declared.docstring;
deprecated = declared.deprecated;
loc = declared.extentLoc;
}
:: !items)
| Tpat_variant (_, Some p, _) -> handlePattern [] p
| Tpat_variant (_, None, _) | Tpat_any | Tpat_constant _ -> ()
in
Expand Down
22 changes: 16 additions & 6 deletions analysis/src/ProcessExtra.ml
Original file line number Diff line number Diff line change
Expand Up @@ -377,22 +377,32 @@ let pat ~(file : File.t) ~env ~extra (iter : Tast_iterator.iterator)
| Tpackage (path, _, _) -> Some path
| _ -> None
in
let addForPattern stamp name =
let addForDeclaredPattern ~stamp ~name ~extent ~item ~attributes =
if Stamps.findValue file.stamps stamp = None then (
let declared =
ProcessAttributes.newDeclared ~name ~stamp ~modulePath:NotVisible
~extent:pattern.pat_loc ~item:pattern.pat_type false
pattern.pat_attributes
~extent ~item false attributes
in
Stamps.addValue file.stamps stamp declared;
addReference ~extra stamp name.loc;
addLocItem extra name.loc
(Typed (name.txt, pattern.pat_type, Definition (stamp, Value))))
(Typed (name.txt, item, Definition (stamp, Value))))
in
let addForPattern stamp name =
addForDeclaredPattern ~stamp ~name ~extent:pattern.pat_loc
~item:pattern.pat_type ~attributes:pattern.pat_attributes
in
(* Log.log("Entering pattern " ++ Utils.showLocation(pat_loc)); *)
(match pattern.pat_desc with
| Tpat_record (items, _) ->
addForRecord ~env ~extra ~recordType:pattern.pat_type items
| Tpat_record (items, _, rest) -> (
addForRecord ~env ~extra ~recordType:pattern.pat_type items;
match rest with
| None -> ()
| Some rest ->
addForDeclaredPattern
~stamp:(Ident.binding_time rest.rest_ident)
~name:rest.rest_name ~extent:rest.rest_name.loc ~item:rest.rest_type
~attributes:pattern.pat_attributes)
| Tpat_construct (lident, constructor, _) ->
addForConstructor ~env ~extra pattern.pat_type lident constructor
| Tpat_alias (_inner, ident, name) -> (
Expand Down
2 changes: 1 addition & 1 deletion analysis/src/SemanticTokens.ml
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ let command ~debug ~emitter ~path =
| Ppat_construct ({txt = Lident ("true" | "false")}, _) ->
(* Don't emit true or false *)
Ast_iterator.default_iterator.pat iterator p
| Ppat_record (cases, _) ->
| Ppat_record (cases, _, _rest) ->
Ext_list.iter cases (fun {lid = label} ->
emitter |> emitRecordLabel ~label ~debug);
Ast_iterator.default_iterator.pat iterator p
Expand Down
3 changes: 2 additions & 1 deletion analysis/src/SignatureHelp.ml
Original file line number Diff line number Diff line change
Expand Up @@ -664,7 +664,8 @@ let signatureHelp ~path ~pos ~currentFile ~debug ~allowForConstructorPayloads =
match tupleItemWithCursor with
| None -> -1
| Some i -> i)
| `ConstructorPat (_, {ppat_desc = Ppat_record (fields, _)}) -> (
| `ConstructorPat (_, {ppat_desc = Ppat_record (fields, _, _rest)})
-> (
let fieldNameWithCursor =
fields
|> List.find_map
Expand Down
2 changes: 1 addition & 1 deletion analysis/src/Xform.ml
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ module IfThenElse = struct
in
match listToPat ~itemToPat items with
| None -> None
| Some patItems -> Some (mkPat (Ppat_record (patItems, Closed))))
| Some patItems -> Some (mkPat (Ppat_record (patItems, Closed, None))))
| Pexp_record (_, Some _) -> None
| _ -> None

Expand Down
4 changes: 2 additions & 2 deletions compiler/common/pattern_printer.ml
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ let untype typed =
| Tpat_variant (label, p_opt, _row_desc) ->
let arg = Option.map loop p_opt in
mkpat (Ppat_variant (label, arg))
| Tpat_record (subpatterns, closed_flag) ->
| Tpat_record (subpatterns, closed_flag, _rest) ->
let fields, saw_optional_rewrite =
List.fold_right
(fun (_, lbl, p, opt) (fields, saw_optional_rewrite) ->
Expand All @@ -97,7 +97,7 @@ let untype typed =
subpatterns ([], false)
in
let closed_flag = if saw_optional_rewrite then Closed else closed_flag in
mkpat (Ppat_record (fields, closed_flag))
mkpat (Ppat_record (fields, closed_flag, None))
| Tpat_array lst -> mkpat (Ppat_array (List.map loop lst))
in
loop typed
Expand Down
2 changes: 1 addition & 1 deletion compiler/core/lam_analysis.ml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ let rec no_side_effects (lam : Lam.t) : bool =
(* whether it's mutable or not *)
| Pfield _ | Pval_from_option | Pval_from_option_not_nest
(* NOP The compiler already [t option] is the same as t *)
| Pduprecord
| Pduprecord | Precord_spread_new _
(* generic primitives *)
| Pobjcomp _ | Pobjorder | Pobjmin | Pobjmax | Pobjtag | Pobjsize
(* bool primitives *)
Expand Down
24 changes: 24 additions & 0 deletions compiler/core/lam_compile_primitive.ml
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,30 @@ let translate output_prefix loc (cxt : Lam_compile_context.t)
match args with
| [e1] -> E.obj ~dup:e1 []
| _ -> assert false)
| Precord_spread_new excluded -> (
match args with
| [e1] ->
(* Generate: (({field1: __unused0, ...__rest}) => __rest)(source)
This uses JS destructuring to cleanly extract the rest while
safely handling quoted property names and the empty-exclusion case. *)
let excluded_bindings =
List.mapi
(fun i field ->
let field = Js_dump_property.property_key (Js_op.Lit field) in
Printf.sprintf "%s: __unused%d" field i)
excluded
in
let destructured =
match excluded_bindings with
| [] -> "...__rest"
| _ -> String.concat ", " excluded_bindings ^ ", ...__rest"
in
let code = Printf.sprintf "(({%s}) => __rest)" destructured in
E.call
~info:{arity = Full; call_info = Call_na; call_transformed_jsx = false}
(E.raw_js_code (Exp (Js_function {arity = 1; arrow = true})) code)
[e1]
| _ -> assert false)
| Phash -> (
match args with
| [e1; e2; e3; e4] -> E.runtime_call Primitive_modules.hash "hash" args
Expand Down
2 changes: 2 additions & 0 deletions compiler/core/lam_convert.ml
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,8 @@ let lam_prim ~primitive:(p : Lambda.primitive) ~args loc : Lam.t =
| Pfield (id, info) -> prim ~primitive:(Pfield (id, info)) ~args loc
| Psetfield (id, info) -> prim ~primitive:(Psetfield (id, info)) ~args loc
| Pduprecord -> prim ~primitive:Pduprecord ~args loc
| Precord_spread_new excluded ->
prim ~primitive:(Precord_spread_new excluded) ~args loc
| Praise _ -> prim ~primitive:Praise ~args loc
| Pobjcomp x -> prim ~primitive:(Pobjcomp x) ~args loc
| Pobjorder -> prim ~primitive:Pobjorder ~args loc
Expand Down
7 changes: 4 additions & 3 deletions compiler/core/lam_primitive.ml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ type t =
| Psetfield of int * Lam_compat.set_field_dbg_info
(* could have field info at least for record *)
| Pduprecord
| Precord_spread_new of string list
(* External call *)
| Pjs_call of {
prim_name: string;
Expand Down Expand Up @@ -226,9 +227,9 @@ let eq_primitive_approx (lhs : t) (rhs : t) =
| Pnull_to_opt | Pnull_undefined_to_opt | Pis_null | Pis_not_none | Psome
| Psome_not_nest | Pis_undefined | Pis_null_undefined | Pimport | Ptypeof
| Pfn_arity | Pis_poly_var_block | Pdebugger | Pinit_mod | Pupdate_mod
| Pduprecord | Pmakearray | Parraylength | Parrayrefu | Parraysetu
| Parrayrefs | Parraysets | Pjs_fn_make_unit | Pjs_fn_method | Phash
| Phash_mixstring | Phash_mixint | Phash_finalmix ->
| Pduprecord | Precord_spread_new _ | Pmakearray | Parraylength | Parrayrefu
| Parraysetu | Parrayrefs | Parraysets | Pjs_fn_make_unit | Pjs_fn_method
| Phash | Phash_mixstring | Phash_mixint | Phash_finalmix ->
rhs = lhs
| Pcreate_extension a -> (
match rhs with
Expand Down
1 change: 1 addition & 0 deletions compiler/core/lam_primitive.mli
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type t =
| Pfield of int * Lambda.field_dbg_info
| Psetfield of int * Lambda.set_field_dbg_info
| Pduprecord
| Precord_spread_new of string list
| Pjs_call of {
(* Location.t * [loc] is passed down *)
prim_name: string;
Expand Down
2 changes: 2 additions & 0 deletions compiler/core/lam_print.ml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ let primitive ppf (prim : Lam_primitive.t) =
let instr = "setfield " in
fprintf ppf "%s%i" instr n
| Pduprecord -> fprintf ppf "duprecord"
| Precord_spread_new excluded ->
fprintf ppf "record_spread_new(%s)" (String.concat ", " excluded)
| Pjs_call {prim_name} -> fprintf ppf "%s[js]" prim_name
| Pjs_object_create _ -> fprintf ppf "[js.obj]"
| Praise -> fprintf ppf "raise"
Expand Down
7 changes: 6 additions & 1 deletion compiler/ext/warnings.ml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ type t =
| Bs_toplevel_expression_unit of
(string * top_level_unit_help) option (* 109 *)
| Bs_todo of string option (* 110 *)
| Bs_record_rest_empty (* 111 *)

(* If you remove a warning, leave a hole in the numbering. NEVER change
the numbers of existing warnings.
Expand Down Expand Up @@ -154,8 +155,9 @@ let number = function
| Bs_uninterpreted_delimiters _ -> 108
| Bs_toplevel_expression_unit _ -> 109
| Bs_todo _ -> 110
| Bs_record_rest_empty -> 111

let last_warning_number = 110
let last_warning_number = 111

let letter_all =
let rec loop i = if i = 0 then [] else i :: loop (i - 1) in
Expand Down Expand Up @@ -532,6 +534,9 @@ let message = function
`%s->ignore`"
help_text help_text
| _ -> "")
| Bs_record_rest_empty ->
"All fields of the rest type are already present in the explicit pattern. \
The rest record will always be empty."
| Bs_todo maybe_text ->
(match maybe_text with
| None -> "Todo found."
Expand Down
1 change: 1 addition & 0 deletions compiler/ext/warnings.mli
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ type t =
| Bs_toplevel_expression_unit of
(string * top_level_unit_help) option (* 109 *)
| Bs_todo of string option (* 110 *)
| Bs_record_rest_empty (* 111 *)

val parse_options : bool -> string -> unit

Expand Down
2 changes: 1 addition & 1 deletion compiler/frontend/ast_tuple_pattern_flatten.ml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ let flattern_tuple_pattern_vb (self : Bs_ast_mapper.mapper)
}
:: acc)
| _ -> {pvb_pat; pvb_expr; pvb_loc = vb.pvb_loc; pvb_attributes} :: acc)
| Ppat_record (lid_pats, _), Pexp_pack {pmod_desc = Pmod_ident id} ->
| Ppat_record (lid_pats, _, _rest), Pexp_pack {pmod_desc = Pmod_ident id} ->
Ext_list.map_append lid_pats acc (fun {lid; x = pat} ->
match lid.txt with
| Lident s ->
Expand Down
6 changes: 5 additions & 1 deletion compiler/frontend/bs_ast_mapper.ml
Original file line number Diff line number Diff line change
Expand Up @@ -425,8 +425,12 @@ module P = struct
| Ppat_construct (l, p) ->
construct ~loc ~attrs (map_loc sub l) (map_opt (sub.pat sub) p)
| Ppat_variant (l, p) -> variant ~loc ~attrs l (map_opt (sub.pat sub) p)
| Ppat_record (lpl, cf) ->
| Ppat_record (lpl, cf, rest) ->
record ~loc ~attrs
?rest:
(match rest with
| None -> None
| Some p -> Some (sub.pat sub p))
(List.map
(fun {lid; x = p; opt} ->
{lid = map_loc sub lid; x = sub.pat sub p; opt})
Expand Down
2 changes: 1 addition & 1 deletion compiler/ml/ast_helper.ml
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ module Pat = struct
let tuple ?loc ?attrs a = mk ?loc ?attrs (Ppat_tuple a)
let construct ?loc ?attrs a b = mk ?loc ?attrs (Ppat_construct (a, b))
let variant ?loc ?attrs a b = mk ?loc ?attrs (Ppat_variant (a, b))
let record ?loc ?attrs a b = mk ?loc ?attrs (Ppat_record (a, b))
let record ?loc ?attrs ?rest a b = mk ?loc ?attrs (Ppat_record (a, b, rest))
let array ?loc ?attrs a = mk ?loc ?attrs (Ppat_array a)
let or_ ?loc ?attrs a b = mk ?loc ?attrs (Ppat_or (a, b))
let constraint_ ?loc ?attrs a b = mk ?loc ?attrs (Ppat_constraint (a, b))
Expand Down
1 change: 1 addition & 0 deletions compiler/ml/ast_helper.mli
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ module Pat : sig
val record :
?loc:loc ->
?attrs:attrs ->
?rest:pattern ->
pattern record_element list ->
closed_flag ->
pattern
Expand Down
5 changes: 3 additions & 2 deletions compiler/ml/ast_iterator.ml
Original file line number Diff line number Diff line change
Expand Up @@ -398,12 +398,13 @@ module P = struct
iter_loc sub l;
iter_opt (sub.pat sub) p
| Ppat_variant (_l, p) -> iter_opt (sub.pat sub) p
| Ppat_record (lpl, _cf) ->
| Ppat_record (lpl, _cf, rest) ->
List.iter
(fun {lid; x = pat} ->
iter_loc sub lid;
sub.pat sub pat)
lpl
lpl;
iter_opt (sub.pat sub) rest
| Ppat_array pl -> List.iter (sub.pat sub) pl
| Ppat_or (p1, p2) ->
sub.pat sub p1;
Expand Down
6 changes: 5 additions & 1 deletion compiler/ml/ast_mapper.ml
Original file line number Diff line number Diff line change
Expand Up @@ -389,8 +389,12 @@ module P = struct
| Ppat_construct (l, p) ->
construct ~loc ~attrs (map_loc sub l) (map_opt (sub.pat sub) p)
| Ppat_variant (l, p) -> variant ~loc ~attrs l (map_opt (sub.pat sub) p)
| Ppat_record (lpl, cf) ->
| Ppat_record (lpl, cf, rest) ->
record ~loc ~attrs
?rest:
(match rest with
| None -> None
| Some p -> Some (sub.pat sub p))
(List.map
(fun {lid; x = pat; opt} ->
{lid = map_loc sub lid; x = sub.pat sub pat; opt})
Expand Down
15 changes: 14 additions & 1 deletion compiler/ml/ast_mapper_from0.ml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,18 @@ let map_constant = function

let map_loc sub {loc; txt} = {loc = sub.location sub loc; txt}

let record_rest_attr_name = "res.record_rest"

let get_record_rest_attr attrs_ =
let rec remove_record_rest_attr acc = function
| ({Location.txt = attr_name; _}, Pt.PPat (rest, None)) :: attrs
when attr_name = record_rest_attr_name ->
(Some rest, List.rev_append acc attrs)
| attr :: attrs -> remove_record_rest_attr (attr :: acc) attrs
| [] -> (None, List.rev acc)
in
remove_record_rest_attr [] attrs_

module T = struct
(* Type expressions for the core language *)

Expand Down Expand Up @@ -576,7 +588,8 @@ module P = struct
construct ~loc ~attrs (map_loc sub l) (map_opt (sub.pat sub) p)
| Ppat_variant (l, p) -> variant ~loc ~attrs l (map_opt (sub.pat sub) p)
| Ppat_record (lpl, cf) ->
record ~loc ~attrs
let rest, attrs = get_record_rest_attr attrs in
record ~loc ~attrs ?rest
(Ext_list.map lpl (fun (lid, p) ->
let lid1 = map_loc sub lid in
let p1 = sub.pat sub p in
Expand Down
Loading
Loading