diff --git a/changes/raw-external-arguments.feature.md b/changes/raw-external-arguments.feature.md new file mode 100644 index 00000000000..98fab9a3015 --- /dev/null +++ b/changes/raw-external-arguments.feature.md @@ -0,0 +1 @@ +- Add `%raw(...)` arguments for externals, for example `external assign: (%raw("{}"), t) => t = "Object.assign"`. The string contains the raw JavaScript value supplied by the binding, and the argument is omitted at call sites. The legacy `` @as(json`...`) _ ``, `@as("...") _`, and `@as(1) _` forms now emit a deprecation warning and are formatted as `%raw(...)`. https://github.com/rescript-lang/rescript/pull/8876 diff --git a/compiler/ext/config.ml b/compiler/ext/config.ml index 8b9658a611b..38a5d1aa5e5 100644 --- a/compiler/ext/config.ml +++ b/compiler/ext/config.ml @@ -2,9 +2,9 @@ let cmi_magic_number = "Caml1999I038" (* Magic numbers for marshaled values of the *current* parsetree, whose layout changes across compiler versions. *) -and ast_impl_magic_number = "ResImpl01317" +and ast_impl_magic_number = "ResImpl01318" -and ast_intf_magic_number = "ResIntf01317" +and ast_intf_magic_number = "ResIntf01318" (* Magic numbers of the frozen Parsetree0 (OCaml 4.06) layout used on the external-PPX wire. They must never be written in front of a diff --git a/compiler/frontend/ast_attributes.ml b/compiler/frontend/ast_attributes.ml index ecd78c16218..aa5b144e71f 100644 --- a/compiler/frontend/ast_attributes.ml +++ b/compiler/frontend/ast_attributes.ml @@ -194,16 +194,17 @@ let as_const (attrs : t) = _ ); }; ] -> ( - (* Check that it is a valid object literal. *) + (* Check that it is exactly one JavaScript literal. *) match Classify_function.classify ~check: (pexp_loc, Bs_flow_ast_utils.flow_deli_offset (Some "json")) s with - | Js_literal _ -> Json s + | Js_literal _ when Classify_function.is_fixed_literal s -> Json s | _ -> - Location.raise_errorf ~loc:pexp_loc "an object literal expected") + Location.raise_errorf ~loc:pexp_loc + "The json payload must be a JavaScript literal") | _ -> Bs_syntaxerr.err loc Expect_int_or_string_or_json_literal))) let locg = Location.none diff --git a/compiler/frontend/ast_external_process.ml b/compiler/frontend/ast_external_process.ml index 2efd51690a7..9527a4492e3 100644 --- a/compiler/frontend/ast_external_process.ml +++ b/compiler/frontend/ast_external_process.ml @@ -399,12 +399,32 @@ type response = { } (* A fixed argument means what [@as(value) _] means, so decode it with the - same reader. *) -let fixed_arg_type + same reader. [%raw] is JavaScript source and must be exactly one literal. + As for [@as(value) _], attributes such as [@ignore] have no effect next to + the value, so they are reported as unused. *) +let fixed_arg_type ~attrs ((value, loc) : Parsetree.fixed_value Location.loc * Location.t) = - match Ast_attributes.as_const [Ast_fixed_arg.to_attribute ~loc value] with - | Some cst -> arg_cst_of_as_const cst - | None -> Bs_syntaxerr.err loc Expect_int_or_string_or_json_literal + let arg_type = + match value.txt with + | Pfixed_raw source -> + let source = Ast_raw.js_source source in + if Classify_function.is_fixed_literal source then + External_arg_spec.Arg_cst (External_arg_spec.cst_json source) + else + Location.raise_errorf ~loc:value.loc + "The %%raw payload must be a JavaScript literal" + | Pfixed_int _ | Pfixed_string _ | Pfixed_template _ | Pfixed_json _ -> ( + match Ast_attributes.as_const [Ast_fixed_arg.to_attribute ~loc value] with + | Some cst -> arg_cst_of_as_const cst + | None -> Bs_syntaxerr.err loc Expect_int_or_string_or_json_literal) + in + Bs_ast_invariant.warn_discarded_unused_attributes attrs; + arg_type + +(* A fixed argument is always passed, so it has nothing to make optional. *) +let reject_optional_fixed + ((_, loc) : Parsetree.fixed_value Location.loc * Location.t) = + Location.raise_errorf ~loc "A %%raw argument cannot be optional" (* The label and type of an argument. A fixed argument is typed as its [_], so the checks for [@as(value) _] apply to it unchanged. Its location ends with @@ -415,7 +435,7 @@ let arg_parts (arg : Parsetree.arg) = | Parg_fixed {attrs; lbl; value; loc} -> ( lbl, attrs, - Ast_helper.Typ.any ~loc:(Ast_fixed_arg.underscore_loc loc) (), + Ast_helper.Typ.any ~loc:(Ast_fixed_arg.closing_loc loc) (), Some (value, loc) ) let process_obj (loc : Location.t) (st : external_desc) (prim_name : string) @@ -469,7 +489,7 @@ let process_obj (loc : Location.t) (st : external_desc) (prim_name : string) in let obj_arg_type = match fixed with - | Some fixed -> fixed_arg_type fixed + | Some fixed -> fixed_arg_type ~attrs:param_attrs fixed | None -> refine_obj_arg_type ~nolabel:false ty in match obj_arg_type with @@ -528,7 +548,11 @@ let process_obj (loc : Location.t) (st : external_desc) (prim_name : string) | Some alias -> alias | None -> label in - let obj_arg_type = get_opt_arg_type ~nolabel:false ty in + let obj_arg_type = + match fixed with + | Some fixed -> reject_optional_fixed fixed + | None -> get_opt_arg_type ~nolabel:false ty + in match obj_arg_type with | Ignore -> ( External_arg_spec.empty_kind obj_arg_type, @@ -932,7 +956,7 @@ let handle_attributes (loc : Location.t) (type_annotation : Parsetree.core_type) Ext_list.fold_right arg_types_ty (([], [], 0) : External_arg_spec.params * Parsetree.arg list * int) (fun param_type (arg_type_specs, arg_types, i) -> - let arg_label, _, ty, fixed = arg_parts param_type in + let arg_label, param_attrs, ty, fixed = arg_parts param_type in (if i = 0 && splice then match arg_label with | Optional _ -> @@ -955,7 +979,11 @@ let handle_attributes (loc : Location.t) (type_annotation : Parsetree.core_type) new_arg_types ) = match arg_label with | Optional {txt = s} -> ( - let arg_type = get_opt_arg_type ~nolabel:false ty in + let arg_type = + match fixed with + | Some fixed -> reject_optional_fixed fixed + | None -> get_opt_arg_type ~nolabel:false ty + in match arg_type with | Poly_var _ -> (* ?x:([`x of int ] [@string]) does not make sense *) @@ -967,7 +995,7 @@ let handle_attributes (loc : Location.t) (type_annotation : Parsetree.core_type) | Labelled _ -> ( let arg_type = match fixed with - | Some fixed -> fixed_arg_type fixed + | Some fixed -> fixed_arg_type ~attrs:param_attrs fixed | None -> refine_arg_type ~nolabel:false ty in ( Arg_label, @@ -978,7 +1006,7 @@ let handle_attributes (loc : Location.t) (type_annotation : Parsetree.core_type) | Nolabel -> ( let arg_type = match fixed with - | Some fixed -> fixed_arg_type fixed + | Some fixed -> fixed_arg_type ~attrs:param_attrs fixed | None -> refine_arg_type ~nolabel:true ty in ( Arg_empty, diff --git a/compiler/ml/ast_fixed_arg.ml b/compiler/ml/ast_fixed_arg.ml index c46d7f36f64..7d55dc72394 100644 --- a/compiler/ml/ast_fixed_arg.ml +++ b/compiler/ml/ast_fixed_arg.ml @@ -1,7 +1,7 @@ open Parsetree let of_payload (payload : payload) : fixed_value Location.loc option = - match payload with + match Ast_payload.unwrap_payload_expression payload with (* A payload with attributes stays an attribute, which keeps them. *) | PStr [ @@ -39,6 +39,9 @@ let to_expression ?(loc = Location.none) (value : fixed_value) : expression = Ast_helper.Exp.tagged_template ~loc (Ast_helper.Exp.ident ~loc (Location.mkloc (Longident.Lident "json") loc)) [source] [] + | Pfixed_raw source -> + Ast_helper.Exp.constant ~loc + (Ast_helper.Const.string (Ast_raw.js_source source)) let to_attribute ?(loc = Location.none) (value : fixed_value Location.loc) : attribute = @@ -49,11 +52,10 @@ let to_attribute ?(loc = Location.none) (value : fixed_value Location.loc) : (to_expression ~loc:value.loc value.txt); ] ) -(* [@as(value) _] on a direct argument becomes [Parg_fixed]. An argument with - an invalid payload stays as written, so external processing reports it as - before. So do the rare arguments that [Parg_fixed] could not print back as - written: a [_] with other attributes too, and a braced payload such as - [@as({1})]. *) +(* [@as(value) _] on a direct argument becomes [Parg_fixed], which prints as + [%raw(...)]. The [_]'s other attributes become the argument's. An argument + with an invalid payload or a second [@as] stays as written, so external + processing reports it as before. *) let normalize_arg (arg : arg) : arg = match arg with | Parg_type @@ -64,8 +66,11 @@ let normalize_arg (arg : arg) : arg = Some (List.rev before, loc, payload, after) | attr :: after -> split (attr :: before) after in + let is_as (({txt}, _) : attribute) = txt = "as" in match split [] ptyp_attributes with - | Some ([], as_loc, payload, []) -> ( + | None -> arg + | Some (_, _, _, after) when List.exists is_as after -> arg + | Some (before, as_loc, payload, after) -> ( match of_payload payload with | None -> arg | Some value -> @@ -73,11 +78,10 @@ let normalize_arg (arg : arg) : arg = if as_loc = Location.none then ptyp_loc else {as_loc with loc_end = ptyp_loc.loc_end} in - Parg_fixed {attrs; lbl; value; loc}) - | Some _ | None -> arg) + Parg_fixed {attrs = attrs @ before @ after; lbl; value; loc})) | Parg_type _ | Parg_fixed _ -> arg -let underscore_loc (loc : Location.t) = +let closing_loc (loc : Location.t) = if loc.loc_ghost then loc else { @@ -93,6 +97,49 @@ let as_name_loc (loc : Location.t) = loc_end = {loc.loc_start with pos_cnum = loc.loc_start.pos_cnum + 3}; } +let quote_js_string semantic = + "\"" ^ String_literal.encode_js_string semantic ^ "\"" + +(* Spell a ReScript integer literal as a JavaScript one. Underscore separators + need ES2021, and a decimal literal with a leading zero is a legacy octal + literal, which strict-mode JavaScript rejects. *) +let js_integer_source source = + let source = String.concat "" (String.split_on_char '_' source) in + let sign, digits = + if source <> "" && source.[0] = '-' then + ("-", String.sub source 1 (String.length source - 1)) + else ("", source) + in + let length = String.length digits in + let has_radix_prefix = + length > 1 + && digits.[0] = '0' + && + match digits.[1] with + | 'x' | 'X' | 'o' | 'O' | 'b' | 'B' -> true + | _ -> false + in + if has_radix_prefix then sign ^ digits + else + let rec first_significant i = + if i < length - 1 && digits.[i] = '0' then first_significant (i + 1) + else i + in + let start = first_significant 0 in + sign ^ String.sub digits start (length - start) + +let js_source (value : fixed_value) = + match value with + | Pfixed_int source -> js_integer_source source.txt + | Pfixed_string payload -> + quote_js_string (String_literal.string_semantic payload) + | Pfixed_template source -> ( + match String_literal.decode_js_template_escapes source.txt with + | Some semantic -> quote_js_string semantic + | None -> "`" ^ source.txt ^ "`") + | Pfixed_json source -> source.txt + | Pfixed_raw source -> Ast_raw.js_source source + let normalize_external_type (typ : core_type) : core_type = match typ.ptyp_desc with | Ptyp_arrow {params; ret} -> diff --git a/compiler/ml/ast_fixed_arg.mli b/compiler/ml/ast_fixed_arg.mli index 71bef131619..6afde316b84 100644 --- a/compiler/ml/ast_fixed_arg.mli +++ b/compiler/ml/ast_fixed_arg.mli @@ -2,7 +2,7 @@ val of_payload : Parsetree.payload -> Parsetree.fixed_value Location.loc option (** The fixed value denoted by an [@as] payload: an integer, a string, a - non-interpolated backquoted string, or a [json] literal. *) + non-interpolated backquoted string, or a [json] literal, braced or not. *) val to_expression : ?loc:Location.t -> Parsetree.fixed_value -> Parsetree.expression @@ -12,8 +12,12 @@ val to_attribute : ?loc:Location.t -> Parsetree.fixed_value Location.loc -> Parsetree.attribute (** The [@as] attribute that spells [value], with its name located at [loc]. *) -val underscore_loc : Location.t -> Location.t -(** The location of the [_] that ends a fixed argument located at [loc]. *) +val closing_loc : Location.t -> Location.t +(** The location of the last character of a fixed argument located at [loc]: + the [)] of [%raw(...)] or the [_] of [@as(value) _]. *) + +val js_source : Parsetree.fixed_value -> string +(** The JavaScript source of a fixed value, as [%raw] spells it. *) val as_name_loc : Location.t -> Location.t (** The location of the [@as] that starts a fixed argument located at [loc]. *) diff --git a/compiler/ml/ast_iterator.ml b/compiler/ml/ast_iterator.ml index 29617acb1ec..eb38b64ac7a 100644 --- a/compiler/ml/ast_iterator.ml +++ b/compiler/ml/ast_iterator.ml @@ -110,6 +110,7 @@ module T = struct match value.txt with | Pfixed_int source | Pfixed_template source | Pfixed_json source -> sub.location sub source.loc + | Pfixed_raw source -> sub.location sub (Ast_raw.source_loc source) | Pfixed_string _ -> ())) params; sub.typ sub ret diff --git a/compiler/ml/ast_mapper.ml b/compiler/ml/ast_mapper.ml index b3652f28faf..dca59ae70aa 100644 --- a/compiler/ml/ast_mapper.ml +++ b/compiler/ml/ast_mapper.ml @@ -115,6 +115,7 @@ module T = struct | Pfixed_template source -> Pfixed_template (map_loc sub source) | Pfixed_json source -> Pfixed_json (map_loc sub source) + | Pfixed_raw source -> Pfixed_raw (map_raw_source sub source) in Parsetree.Parg_fixed { diff --git a/compiler/ml/ast_mapper_to0.ml b/compiler/ml/ast_mapper_to0.ml index d469b0e0cfb..2e292492ee3 100644 --- a/compiler/ml/ast_mapper_to0.ml +++ b/compiler/ml/ast_mapper_to0.ml @@ -200,22 +200,33 @@ module T = struct match arg with | Parg_type {attrs; lbl; typ} -> (lbl, attrs, sub.typ sub typ) | Parg_fixed {attrs; lbl; value; loc} -> - (* v0 spells a fixed argument [_ [@as value]], with [json] as a - [json]-delimited string, as before [json] was an ordinary - tagged template. [@as] and [_] get their own locations, from - which [Ast_fixed_arg] restores the argument's. *) + (* v0 spells a fixed argument [_ [@as value]], with JavaScript + source as a [json]-delimited string, as before [%raw] and + before [json] was an ordinary tagged template. [@as] and [_] + get their own locations, from which [Ast_fixed_arg] restores + the argument's. *) let as_loc = Ast_fixed_arg.as_name_loc loc in let as_attr = match value.txt with - | Pfixed_json source -> + | (Pfixed_json _ | Pfixed_raw _) as fixed -> + let source, source_loc = + match fixed with + | Pfixed_raw source -> + (Ast_raw.js_source source, Ast_raw.source_loc source) + | Pfixed_json source + | Pfixed_int source + | Pfixed_template source -> + (source.txt, source.loc) + | Pfixed_string _ -> assert false + in ( {txt = "as"; loc = sub.location sub as_loc}, Pt.PStr [ Ast_helper0.Str.eval ~loc:(sub.location sub value.loc) (Ast_helper0.Exp.constant - ~loc:(sub.location sub source.loc) - (Pt.Pconst_string (source.txt, Some "json"))); + ~loc:(sub.location sub source_loc) + (Pt.Pconst_string (source, Some "json"))); ] ) | Pfixed_int _ | Pfixed_string _ | Pfixed_template _ -> sub.attribute sub @@ -224,7 +235,7 @@ module T = struct ( lbl, attrs, Ast_helper0.Typ.any - ~loc:(sub.location sub (Ast_fixed_arg.underscore_loc loc)) + ~loc:(sub.location sub (Ast_fixed_arg.closing_loc loc)) ~attrs:[as_attr] () ) in let lbl = Asttypes.to_noloc arg_lbl in diff --git a/compiler/ml/classify_function.ml b/compiler/ml/classify_function.ml index 1a7b0f4f5ec..966e7814917 100644 --- a/compiler/ml/classify_function.ml +++ b/compiler/ml/classify_function.ml @@ -28,7 +28,11 @@ let rec is_obj_literal (x : _ Flow_ast.Expression.t) : bool = | StringLiteral _ | BooleanLiteral _ | NullLiteral _ | NumberLiteral _ | BigIntLiteral _ | RegExpLiteral _ | ModuleRefLiteral _ -> true - | Unary {operator = Minus; argument} -> is_obj_literal argument + (* Negating anything but a number converts it to a primitive, which can run + a [valueOf] method. *) + | Unary {operator = Minus; argument = _, (NumberLiteral _ | BigIntLiteral _)} + -> + true | Object {properties} -> Ext_list.for_all properties is_literal_kv | Array {elements} -> Ext_list.for_all elements (fun x -> @@ -39,7 +43,16 @@ let rec is_obj_literal (x : _ Flow_ast.Expression.t) : bool = and is_literal_kv (x : _ Flow_ast.Expression.Object.property) = match x with - | Property (_, Init {value}) -> is_obj_literal value + | Property + ( _, + Init + { + key = + StringLiteral _ | NumberLiteral _ | BigIntLiteral _ | Identifier _; + value; + } ) -> + is_obj_literal value + (* A computed key such as [[f()]] runs code. *) | _ -> false let classify_exp (prog : _ Flow_ast.Expression.t) : Js_raw_info.exp = @@ -104,6 +117,29 @@ let classify ?(check : (Location.t * int) option) (prog : string) : | Some _, [] | None, [] -> classify_exp prog | None, _ :: _ -> Js_exp_unknown +let is_fixed_literal (source : string) : bool = + let open Parser_flow in + (* The output is strict mode JavaScript, which rejects legacy octal literals + and escapes. *) + let parse_options = + Some {Parser_env.default_parse_options with use_strict = true} + in + let env = Parser_env.init_env ~parse_options None source in + match + do_parse env + (fun env -> + let expression = Parse.expression env in + let token = Parser_env.Peek.token env in + (* Eating the last token records the comments lexed before it, + including those already attached to [expression]. *) + Parser_env.Eat.token env; + (expression, token, Parser_env.comments env)) + false + with + | (expression, Token.T_EOF, []), [] -> is_obj_literal expression + | _ -> false + | exception _ -> false + let classify_stmt (prog : string) : Js_raw_info.stmt = let result = Parser_flow.parse_program false None prog in match fst result with diff --git a/compiler/ml/classify_function.mli b/compiler/ml/classify_function.mli index 38b1548515e..f32d05485a4 100644 --- a/compiler/ml/classify_function.mli +++ b/compiler/ml/classify_function.mli @@ -27,3 +27,8 @@ val classify : ?check:Location.t * int -> string -> Js_raw_info.exp val classify_exp : (Loc.t, Loc.t) Flow_ast.Expression.t -> Js_raw_info.exp val classify_stmt : string -> Js_raw_info.stmt + +val is_fixed_literal : string -> bool +(** Whether [source] is exactly one JavaScript literal. The generated code + embeds the source verbatim, so trailing tokens, which [classify] ignores, + and comments, which could swallow the code that follows, are rejected. *) diff --git a/compiler/ml/parsetree.ml b/compiler/ml/parsetree.ml index f9a008d7b80..6e71381d665 100644 --- a/compiler/ml/parsetree.ml +++ b/compiler/ml/parsetree.ml @@ -96,10 +96,11 @@ and arg = attrs: attributes; lbl: arg_label; value: fixed_value loc; (* located at the payload *) - loc: Location.t; (* the whole argument, from [@as] to [_] *) + loc: Location.t; (* the whole argument *) } (* An external function argument whose value is fixed by the binding and - omitted at call sites, written [@as(value) _]. *) + omitted at call sites, written [%raw(source)] or, in the deprecated form, + [@as(value) _]. *) and fixed_value = | Pfixed_int of string loc (* [@as(1)]: the integer literal's source *) @@ -107,7 +108,8 @@ and fixed_value = | Pfixed_template of string loc (* [@as(`s`)]: the source between the backquotes *) | Pfixed_json of string loc -(* [@as(json`{}`)]: the JavaScript source between the backquotes *) + (* [@as(json`{}`)]: the JavaScript source between the backquotes *) + | Pfixed_raw of raw_source (* [%raw("{}")] *) and core_type_desc = | Ptyp_any (* _ *) diff --git a/compiler/ml/pprintast.ml b/compiler/ml/pprintast.ml index 11d9bbd5e97..3d1f80b1e54 100644 --- a/compiler/ml/pprintast.ml +++ b/compiler/ml/pprintast.ml @@ -290,24 +290,19 @@ let tyvar_loc f str = pp f "'%s" str.txt let string_quot f x = pp f "`%s" x let rec type_with_label ctxt f arg = - let attrs, lbl, typ = - match arg with - | Parg_type {attrs; lbl; typ} -> (attrs, lbl, typ) - | Parg_fixed {attrs; lbl; value; loc} -> - (* Printed in its legacy spelling, [_ [@as value]]. *) - ( attrs, - lbl, - Ast_helper.Typ.any ~loc - ~attrs:[Ast_fixed_arg.to_attribute ~loc value] - () ) + let label lbl = + match lbl with + | Nolabel -> "" + | Labelled {txt = s} -> s ^ ":" + | Optional {txt = s} -> "?" ^ s ^ ":" in - match lbl with - | Nolabel -> pp f "%a%a" (core_type1 ctxt) typ (attributes ctxt) attrs - (* otherwise parenthesize *) - | Labelled {txt = s} -> - pp f "%s:%a%a" s (core_type1 ctxt) typ (attributes ctxt) attrs - | Optional {txt = s} -> - pp f "?%s:%a%a" s (core_type1 ctxt) typ (attributes ctxt) attrs + match arg with + | Parg_type {attrs; lbl; typ} -> + pp f "%s%a%a" (label lbl) (core_type1 ctxt) typ (attributes ctxt) attrs + | Parg_fixed {attrs; lbl; value} -> + pp f "%s%%raw(%a)%a" (label lbl) print_quoted_string_with_byte_width + (Ast_fixed_arg.js_source value.txt) + (attributes ctxt) attrs and core_type ctxt f x = if x.ptyp_attributes <> [] then diff --git a/compiler/ml/printast.ml b/compiler/ml/printast.ml index aae95925418..0ac87f85ff1 100644 --- a/compiler/ml/printast.ml +++ b/compiler/ml/printast.ml @@ -126,6 +126,13 @@ let arg_label_loc i ppf = function | Optional {txt = s} -> line i ppf "Optional \"%s\"\n" s | Labelled {txt = s} -> line i ppf "Labelled \"%s\"\n" s +let raw_source i ppf (source : raw_source) = + match source with + | Raw_string {txt; loc} -> + line i ppf "Raw_string %S %a\n" txt fmt_location loc + | Raw_template {txt; loc} -> + line i ppf "Raw_template %S %a\n" txt fmt_location loc + let fixed_value i ppf (x : fixed_value) = match x with | Pfixed_int source -> @@ -138,13 +145,9 @@ let fixed_value i ppf (x : fixed_value) = line i ppf "Pfixed_template %S %a\n" source.txt fmt_location source.loc | Pfixed_json source -> line i ppf "Pfixed_json %S %a\n" source.txt fmt_location source.loc - -let raw_source i ppf (source : raw_source) = - match source with - | Raw_string {txt; loc} -> - line i ppf "Raw_string %S %a\n" txt fmt_location loc - | Raw_template {txt; loc} -> - line i ppf "Raw_template %S %a\n" txt fmt_location loc + | Pfixed_raw source -> + line i ppf "Pfixed_raw\n"; + raw_source (i + 1) ppf source let rec core_type i ppf x = line i ppf "core_type %a\n" fmt_location x.ptyp_loc; diff --git a/compiler/syntax/src/res_comments_table.ml b/compiler/syntax/src/res_comments_table.ml index f69fcf96894..e1ca97a53c3 100644 --- a/compiler/syntax/src/res_comments_table.ml +++ b/compiler/syntax/src/res_comments_table.ml @@ -2258,8 +2258,7 @@ and walk_type_parameters type_parameters t comments = let lbl, end_loc = match parameter with | Parsetree.Parg_type {lbl; typ} -> (lbl, typ.ptyp_loc) - | Parsetree.Parg_fixed {lbl; loc} -> - (lbl, Ast_fixed_arg.underscore_loc loc) + | Parsetree.Parg_fixed {lbl; loc} -> (lbl, Ast_fixed_arg.closing_loc loc) in let lbl_loc = Asttypes.get_lbl_loc lbl in if lbl_loc <> Location.none then {lbl_loc with loc_end = end_loc.loc_end} @@ -2277,8 +2276,8 @@ and walk_type_parameter parameter t comments = walk_core_type typexpr t inside_typ; attach t.trailing typexpr.ptyp_loc after_typ | Parsetree.Parg_fixed {loc} -> - (* Like the [_] it is written with, which is where its comments go. *) - let loc = Ast_fixed_arg.underscore_loc loc in + (* Like a type, which is what its comments go around. *) + let loc = Ast_fixed_arg.closing_loc loc in let before, after = partition_leading_trailing comments loc in attach t.leading loc before; attach t.trailing loc after diff --git a/compiler/syntax/src/res_core.ml b/compiler/syntax/src/res_core.ml index 222cffc834d..7c37d6ca295 100644 --- a/compiler/syntax/src/res_core.ml +++ b/compiler/syntax/src/res_core.ml @@ -288,6 +288,80 @@ type type_parameter = { start_pos: Lexing.position; } +let reject_raw_fixed_extensions p (typ : Parsetree.core_type) = + let super = Ast_iterator.default_iterator in + let iterator = + { + super with + typ = + (fun self typ -> + (match typ.ptyp_desc with + | Ptyp_extension ({txt = "raw"; loc}, _) -> + Parser.err ~start_pos:loc.loc_start ~end_pos:loc.loc_end p + (Diagnostics.message + "%raw is only allowed as a direct external function argument") + | _ -> ()); + super.typ self typ); + } + in + iterator.typ iterator typ + +(* Read an external's fixed arguments: [%raw(source)] and the deprecated + [@as(value) _]. *) +let normalize_external_fixed_parameters p (typ : Parsetree.core_type) = + let typ = + match (Ast_fixed_arg.normalize_external_type typ).ptyp_desc with + | Ptyp_arrow {params; ret} -> + let params = + List.map + (fun (arg : Parsetree.arg) -> + match arg with + | Parg_fixed {loc} -> + Parser.warn p loc + (Warnings.Deprecated + ( "The fixed-value `@as(...) _` external argument syntax is \ + deprecated. Use `%raw(...)` instead.", + loc, + loc, + false )); + arg + | Parg_type + ({ + typ = + { + ptyp_desc = + Ptyp_extension (({txt = "raw"} as id), payload); + ptyp_loc; + ptyp_attributes; + }; + } as typed_arg) -> ( + match Ast_raw.of_payload payload with + | Some source -> + Parg_fixed + { + attrs = typed_arg.attrs @ ptyp_attributes; + lbl = typed_arg.lbl; + value = + { + txt = Pfixed_raw source; + loc = Ast_raw.payload_loc source; + }; + loc = ptyp_loc; + } + | None -> + Parser.err ~start_pos:id.loc.loc_start ~end_pos:id.loc.loc_end p + (Diagnostics.message + "The %raw extension can only be applied to a string"); + arg) + | Parg_type _ -> arg) + params + in + {typ with ptyp_desc = Ptyp_arrow {params; ret}} + | _ -> typ + in + reject_raw_fixed_extensions p typ; + typ + type typ_def_or_ext = | TypeDef of { rec_flag: Asttypes.rec_flag; @@ -6289,7 +6363,7 @@ and parse_external_def ~attrs ~start_pos p = in let typ_expr = parse_external_type_expr ~current_type_name_path ~inline_types_context p - |> Ast_fixed_arg.normalize_external_type + |> normalize_external_fixed_parameters p in let equal_start = Parser.start_pos p in let equal_end = Parser.end_pos p in diff --git a/compiler/syntax/src/res_printer.ml b/compiler/syntax/src/res_printer.ml index 3cb9cd6d65e..8a60b776b2c 100644 --- a/compiler/syntax/src/res_printer.ml +++ b/compiler/syntax/src/res_printer.ml @@ -1983,10 +1983,7 @@ and print_typ_expr ?inline_record_definitions ~(state : State.t) in match args with | [] -> Doc.nil - | [ - (( Parg_type {attrs = []; lbl = Nolabel} - | Parg_fixed {attrs = []; lbl = Nolabel} ) as arg); - ] -> + | [Parg_type {attrs = []; lbl = Nolabel; typ}] -> let has_attrs_before = not (attrs_before = []) in let attrs = if has_attrs_before then @@ -1994,16 +1991,12 @@ and print_typ_expr ?inline_record_definitions ~(state : State.t) else Doc.nil in let typ_doc = - match arg with - | Parg_type {typ} -> ( - let doc = - print_typ_expr ?inline_record_definitions ~state typ cmt_tbl - in - match typ.ptyp_desc with - | Ptyp_arrow _ | Ptyp_tuple _ | Ptyp_alias _ -> add_parens doc - | _ -> doc) - | Parg_fixed _ -> - print_type_parameter ?inline_record_definitions ~state arg cmt_tbl + let doc = + print_typ_expr ?inline_record_definitions ~state typ cmt_tbl + in + match typ.ptyp_desc with + | Ptyp_arrow _ | Ptyp_tuple _ | Ptyp_alias _ -> add_parens doc + | _ -> doc in Doc.group (Doc.concat @@ -2404,17 +2397,25 @@ and print_type_parameter ?inline_record_definitions ~state parameter cmt_tbl = print_typ_expr ?inline_record_definitions ~state typ cmt_tbl, typ.ptyp_loc ) | Parsetree.Parg_fixed {attrs; lbl; value; loc} -> - (* Comments go around [@as(...) _], as for the [_] it is written with. *) - let loc = Ast_fixed_arg.underscore_loc loc in - let as_doc, _ = - print_attribute ~state - (Ast_fixed_arg.to_attribute {value with loc = Location.none}) - cmt_tbl - in + (* Comments go around [%raw(...)], as for a type. *) + let loc = Ast_fixed_arg.closing_loc loc in ( attrs, lbl, + (* Printed as [%raw(...)] is as an expression; other values migrate to + their JavaScript source. *) print_comments - (Doc.group (Doc.concat [as_doc; Doc.space; Doc.text "_"])) + (print_extension ~state ~at_module_lvl:false + (Ast_raw.to_extension ~name:"raw" ~name_loc:Location.none + (match value.txt with + | Pfixed_raw source -> source + | Pfixed_int _ | Pfixed_string _ | Pfixed_template _ + | Pfixed_json _ -> + Raw_string + { + txt = Ast_fixed_arg.js_source value.txt; + loc = Location.none; + })) + cmt_tbl) cmt_tbl loc, loc ) in diff --git a/packages/@rescript/belt/src/Belt_Array.res b/packages/@rescript/belt/src/Belt_Array.res index 11afbf34d54..eadf27dce12 100644 --- a/packages/@rescript/belt/src/Belt_Array.res +++ b/packages/@rescript/belt/src/Belt_Array.res @@ -51,7 +51,7 @@ let setExn = setOrThrow @new external makeUninitializedUnsafe: int => array<'a> = "Array" -@send external copy: (t<'a>, @as(0) _) => t<'a> = "slice" +@send external copy: (t<'a>, %raw("0")) => t<'a> = "slice" let swapUnsafe = (xs, i, j) => { let tmp = getUnsafe(xs, i) diff --git a/packages/@rescript/belt/src/Belt_Array.resi b/packages/@rescript/belt/src/Belt_Array.resi index 75cd9fb4b21..a9076874f73 100644 --- a/packages/@rescript/belt/src/Belt_Array.resi +++ b/packages/@rescript/belt/src/Belt_Array.resi @@ -325,7 +325,7 @@ let sliceToEnd: (t<'a>, int) => t<'a> elements as `a`. */ @send -external copy: (t<'a>, @as(0) _) => t<'a> = "slice" +external copy: (t<'a>, %raw("0")) => t<'a> = "slice" /** `fill(arr, ~offset, ~len, x)` modifies `arr` in place, storing `x` in elements diff --git a/packages/@rescript/belt/src/Belt_Int.res b/packages/@rescript/belt/src/Belt_Int.res index 177aae00397..72f86f2fd6d 100644 --- a/packages/@rescript/belt/src/Belt_Int.res +++ b/packages/@rescript/belt/src/Belt_Int.res @@ -14,7 +14,7 @@ external toFloat: int => float = "%identity" external fromFloat: float => int = "%intoffloat" -@val external fromString: (string, @as(10) _) => int = "parseInt" +@val external fromString: (string, %raw("10")) => int = "parseInt" let fromString = i => switch fromString(i) { diff --git a/packages/@rescript/runtime/Stdlib_Array.res b/packages/@rescript/runtime/Stdlib_Array.res index fc639b5a559..3e892e1edea 100644 --- a/packages/@rescript/runtime/Stdlib_Array.res +++ b/packages/@rescript/runtime/Stdlib_Array.res @@ -133,7 +133,7 @@ external toSpliced: (array<'a>, ~start: int, ~remove: int, ~insert: array<'a>) = "toSpliced" @send -external removeInPlace: (array<'a>, int, @as(1) _) => unit = "splice" +external removeInPlace: (array<'a>, int, %raw("1")) => unit = "splice" @send external with: (array<'a>, int, 'a) => array<'a> = "with" diff --git a/packages/@rescript/runtime/Stdlib_Array.resi b/packages/@rescript/runtime/Stdlib_Array.resi index d411a10b54a..c5124b4d680 100644 --- a/packages/@rescript/runtime/Stdlib_Array.resi +++ b/packages/@rescript/runtime/Stdlib_Array.resi @@ -485,7 +485,7 @@ array2 == ["Hello", "Good bye"] // Removes the item at index 1 ``` */ @send -external removeInPlace: (array<'a>, int, @as(1) _) => unit = "splice" +external removeInPlace: (array<'a>, int, %raw("1")) => unit = "splice" /** `with(array, index, value)` returns a copy of `array` where the element at `index` is replaced with `value`. diff --git a/packages/@rescript/runtime/Stdlib_Dict.res b/packages/@rescript/runtime/Stdlib_Dict.res index 04f1b56281a..ac53db1ab85 100644 --- a/packages/@rescript/runtime/Stdlib_Dict.res +++ b/packages/@rescript/runtime/Stdlib_Dict.res @@ -25,14 +25,14 @@ external fromIterable: Stdlib_Iterable.t<(string, 'a)> => dict<'a> = "Object.fro @variadic @val external assignMany: (dict<'a>, array>) => dict<'a> = "Object.assign" -@val external concat: (@as(json`{}`) _, dict<'a>, dict<'a>) => dict<'a> = "Object.assign" +@val external concat: (%raw("{}"), dict<'a>, dict<'a>) => dict<'a> = "Object.assign" @variadic @val -external concatMany: (@as(json`{}`) _, dict<'a>, array>) => dict<'a> = "Object.assign" +external concatMany: (%raw("{}"), dict<'a>, array>) => dict<'a> = "Object.assign" -@variadic @val external concatAll: (@as(json`{}`) _, array>) => dict<'a> = "Object.assign" +@variadic @val external concatAll: (%raw("{}"), array>) => dict<'a> = "Object.assign" -@val external copy: (@as(json`{}`) _, dict<'a>) => dict<'a> = "Object.assign" +@val external copy: (%raw("{}"), dict<'a>) => dict<'a> = "Object.assign" // Use %raw to support for..in which is a ~10% faster than .forEach let forEach: (dict<'a>, 'a => unit) => unit = %raw(`(dict, f) => { diff --git a/packages/@rescript/runtime/Stdlib_Dict.resi b/packages/@rescript/runtime/Stdlib_Dict.resi index 224be3ffefb..b0d54fe4d12 100644 --- a/packages/@rescript/runtime/Stdlib_Dict.resi +++ b/packages/@rescript/runtime/Stdlib_Dict.resi @@ -280,7 +280,7 @@ merged == dict{"firstKey": 2, "someKey": 3} ``` */ @val -external concat: (@as(json`{}`) _, dict<'a>, dict<'a>) => dict<'a> = "Object.assign" +external concat: (%raw("{}"), dict<'a>, dict<'a>) => dict<'a> = "Object.assign" /** `concatMany(target, sources)` [shallowly](https://developer.mozilla.org/en-US/docs/Glossary/Shallow_copy) merges `target` and each dictionary in `sources` into a fresh dictionary, and returns the new dictionary. @@ -301,7 +301,7 @@ merged == dict{"firstKey": 1, "someKey": 3, "someKey2": 4} ``` */ @variadic @val -external concatMany: (@as(json`{}`) _, dict<'a>, array>) => dict<'a> = "Object.assign" +external concatMany: (%raw("{}"), dict<'a>, array>) => dict<'a> = "Object.assign" /** `concatAll(dictionaries)` [shallowly](https://developer.mozilla.org/en-US/docs/Glossary/Shallow_copy) merges all dictionaries in `dictionaries` into a fresh dictionary, and returns the new dictionary. @@ -322,7 +322,7 @@ merged == dict{"firstKey": 1, "someKey": 3, "someKey2": 4} ``` */ @variadic @val -external concatAll: (@as(json`{}`) _, array>) => dict<'a> = "Object.assign" +external concatAll: (%raw("{}"), array>) => dict<'a> = "Object.assign" /** `copy(dictionary)` [shallowly copies](https://developer.mozilla.org/en-US/docs/Glossary/Shallow_copy) the provided dictionary to a new dictionary. @@ -337,7 +337,7 @@ Console.log2(dict->Dict.keysToArray, dict2->Dict.keysToArray) ``` */ @val -external copy: (@as(json`{}`) _, dict<'a>) => dict<'a> = "Object.assign" +external copy: (%raw("{}"), dict<'a>) => dict<'a> = "Object.assign" /** `forEach(dictionary, f)` iterates through all values of the dict. diff --git a/packages/@rescript/runtime/Stdlib_JSON.res b/packages/@rescript/runtime/Stdlib_JSON.res index 50012e67c87..ba3b3a8ce64 100644 --- a/packages/@rescript/runtime/Stdlib_JSON.res +++ b/packages/@rescript/runtime/Stdlib_JSON.res @@ -34,7 +34,7 @@ external parseExnWithReviver: (string, (string, t) => t) => t = "JSON.parse" migrate: JSON.stringify(%insert.unlabelledArgument(0), ~space=%insert.unlabelledArgument(2)), }) @val -external stringifyWithIndent: (t, @as(json`null`) _, int) => string = "JSON.stringify" +external stringifyWithIndent: (t, %raw("null"), int) => string = "JSON.stringify" @deprecated({ reason: "Use `JSON.stringify` with optional parameter instead", migrate: JSON.stringify( @@ -83,7 +83,7 @@ external stringifyAny: ('a, ~replacer: replacer=?, ~space: int=?) => option option = "JSON.stringify" +external stringifyAnyWithIndent: ('a, %raw("null"), int) => option = "JSON.stringify" @deprecated({ reason: "Use `JSON.stringifyAny` with optional parameter instead", migrate: JSON.stringifyAny( diff --git a/packages/@rescript/runtime/Stdlib_JSON.resi b/packages/@rescript/runtime/Stdlib_JSON.resi index 18b3870b494..c92b77fd473 100644 --- a/packages/@rescript/runtime/Stdlib_JSON.resi +++ b/packages/@rescript/runtime/Stdlib_JSON.resi @@ -217,7 +217,7 @@ JSON.stringifyWithIndent(json, 2) ``` */ @deprecated("Use `stringify` with optional parameter instead") @val -external stringifyWithIndent: (t, @as(json`null`) _, int) => string = "JSON.stringify" +external stringifyWithIndent: (t, %raw("null"), int) => string = "JSON.stringify" /** `stringifyWithReplacer(json, replacer)` @@ -442,7 +442,7 @@ switch BigInt.fromInt(0)->JSON.stringifyAny { - Throws a TypeError if the value contains `BigInt`s. */ @deprecated("Use `stringifyAny` with optional parameter instead") @val -external stringifyAnyWithIndent: ('a, @as(json`null`) _, int) => option = "JSON.stringify" +external stringifyAnyWithIndent: ('a, %raw("null"), int) => option = "JSON.stringify" /** `stringifyAnyWithReplacer(json, replacer)` diff --git a/packages/@rescript/runtime/Stdlib_Object.res b/packages/@rescript/runtime/Stdlib_Object.res index 27a314a0d38..152f102dc87 100644 --- a/packages/@rescript/runtime/Stdlib_Object.res +++ b/packages/@rescript/runtime/Stdlib_Object.res @@ -85,7 +85,7 @@ let obj = Object.createWithNull() obj->Object.get("toString") == None ``` */ -@val external createWithNull: (@as(json`null`) _, unit) => {..} = "Object.create" +@val external createWithNull: (%raw("null"), unit) => {..} = "Object.create" /** `createWithNullAndProperties(descriptors)` creates an object with a `null` prototype and defines properties using descriptor objects. @@ -100,7 +100,7 @@ obj->Object.get("name") == Some("banana") obj->Object.get("toString") == None ``` */ -@val external createWithNullAndProperties: (@as(json`null`) _, {..}) => {..} = "Object.create" +@val external createWithNullAndProperties: (%raw("null"), {..}) => {..} = "Object.create" /** `assign(target, source)` copies enumerable own properties from the source to the target, overwriting properties with the same name. It returns the modified target object. A deep clone is not created; properties are copied by reference. @@ -144,7 +144,7 @@ cloned->Object.get("name") == Some("banana") Object.is(original, cloned) == false ``` */ -@val external copy: (@as(json`{}`) _, {..} as 'a) => 'a = "Object.assign" +@val external copy: (%raw("{}"), {..} as 'a) => 'a = "Object.assign" /** `get` gets the value of a property by name. Returns `None` if the property does not exist or has the value `undefined`. Otherwise returns `Some`, including if the value is `null`. diff --git a/packages/@rescript/runtime/Stdlib_RegExp.res b/packages/@rescript/runtime/Stdlib_RegExp.res index 9f1b5736a20..545ea5a9897 100644 --- a/packages/@rescript/runtime/Stdlib_RegExp.res +++ b/packages/@rescript/runtime/Stdlib_RegExp.res @@ -3,8 +3,8 @@ type t module Result = { type t = array> - @get_index external fullMatch: (t, @as(0) _) => string = "" - @send external matches: (t, @as(1) _) => array> = "slice" + @get_index external fullMatch: (t, %raw("0")) => string = "" + @send external matches: (t, %raw("1")) => array> = "slice" @get external index: t => int = "index" @get external input: t => string = "input" } diff --git a/packages/@rescript/runtime/Stdlib_RegExp.resi b/packages/@rescript/runtime/Stdlib_RegExp.resi index f6822678973..16347d7dd33 100644 --- a/packages/@rescript/runtime/Stdlib_RegExp.resi +++ b/packages/@rescript/runtime/Stdlib_RegExp.resi @@ -31,7 +31,7 @@ module Result: { ``` */ @get_index - external fullMatch: (t, @as(0) _) => string = "" + external fullMatch: (t, %raw("0")) => string = "" /** `matches(regExpResult)` returns all matches for `regExpResult`. @@ -53,7 +53,7 @@ module Result: { ``` */ @send - external matches: (t, @as(1) _) => array> = "slice" + external matches: (t, %raw("1")) => array> = "slice" @get external index: t => int = "index" /** diff --git a/packages/dev-playground/src/Bindings.res b/packages/dev-playground/src/Bindings.res index edf48d15251..9c72a1a3bfc 100644 --- a/packages/dev-playground/src/Bindings.res +++ b/packages/dev-playground/src/Bindings.res @@ -240,8 +240,10 @@ module Document = { @val external current: {..} = "document" @get external head: {..} => Dom.element = "head" @get external body: {..} => Dom.element = "body" - @send external createScriptElement: ({..}, @as("script") _) => Dom.element = "createElement" - @send external createTextAreaElement: ({..}, @as("textarea") _) => Dom.element = "createElement" + // The playground builds with a released compiler, so no fixed arguments here. + @send external createElement: ({..}, string) => Dom.element = "createElement" + let createScriptElement = document => document->createElement("script") + let createTextAreaElement = document => document->createElement("textarea") @send @return(nullable) external getElementById: ({..}, string) => option = "getElementById" @send external execCommand: ({..}, string) => bool = "execCommand" @@ -267,7 +269,8 @@ module Location = { module History = { @val @scope(("window", "history")) - external replaceState: (@as(json`null`) _, @as("") _, string) => unit = "replaceState" + external replaceStateWith: (Null.t, string, string) => unit = "replaceState" + let replaceState = url => replaceStateWith(Null.null, "", url) } module Performance = { diff --git a/rewatch/tests/watch/01-watch-recompile.sh b/rewatch/tests/watch/01-watch-recompile.sh index c9ba7184e65..3f77b170d29 100755 --- a/rewatch/tests/watch/01-watch-recompile.sh +++ b/rewatch/tests/watch/01-watch-recompile.sh @@ -22,9 +22,10 @@ success "Watcher Started" # Trigger a recompilation echo 'Console.log("added-by-test")' >> ./packages/main/src/Main.res -# Wait for the compiled JS to show up (can be slow in CI) +# A cold build of the test repo can exceed 20 seconds on Intel macOS CI. +# Poll so faster machines can continue as soon as the output is ready. target=./packages/main/src/Main.mjs -if ! wait_for_file "$target" 40; then +if ! wait_for_file "$target" 60; then error "Expected output not found: $target" ls -la ./packages/main/src || true tail -n 200 rewatch.log || true diff --git a/tests/analysis_tests/tests-reanalyze/deadcode/expected/deadcode.txt b/tests/analysis_tests/tests-reanalyze/deadcode/expected/deadcode.txt index 218b96b02c0..066c1019027 100644 --- a/tests/analysis_tests/tests-reanalyze/deadcode/expected/deadcode.txt +++ b/tests/analysis_tests/tests-reanalyze/deadcode/expected/deadcode.txt @@ -56,7 +56,7 @@ add_type_reference _none_:1:-1 --> ContextOptionalArgs.res:12:14 add_value_reference ContextOptionalArgs.res:12:6 --> React.res:16:0 add_value_reference ContextOptionalArgs.res:22:6 --> ContextOptionalArgs.res:2:6 - add_value_reference ContextOptionalArgs.res:22:6 --> React.res:250:0 + add_value_reference ContextOptionalArgs.res:22:6 --> React.res:249:0 addDeclaration_ Value +dispatchNotification ContextOptionalArgs.res:28:8 path:+ContextOptionalArgs.ComponentUsingAction add_value_reference ContextOptionalArgs.res:28:8 --> ContextOptionalArgs.res:22:6 add_value_reference ContextOptionalArgs.res:35:4 --> React.res:3:0 diff --git a/tests/analysis_tests/tests/src/CompletionJsx.res b/tests/analysis_tests/tests/src/CompletionJsx.res index 655d61298d3..6ad77f6a33f 100644 --- a/tests/analysis_tests/tests/src/CompletionJsx.res +++ b/tests/analysis_tests/tests/src/CompletionJsx.res @@ -56,7 +56,7 @@ module IntrinsicElementLowercase = { type props = {name?: string, age?: int} @module("react") - external make: (@as("mesh") _, props) => Jsx.element = "createElement" + external make: (%raw(`"mesh"`), props) => Jsx.element = "createElement" } // option unit>) => unit = "useEffect" @module("react") -external useEffect0: (unit => option unit>, @as(json`[]`) _) => unit = "useEffect" +external useEffect0: (unit => option unit>, %raw("[]")) => unit = "useEffect" @module("react") external useEffect1: (unit => option unit>, array<'a>) => unit = "useEffect" @module("react") @@ -145,8 +145,7 @@ external useEffect7: (unit => option unit>, ('a, 'b, 'c, 'd, 'e, 'f, 'g) @module("react") external useLayoutEffect: (unit => option unit>) => unit = "useLayoutEffect" @module("react") -external useLayoutEffect0: (unit => option unit>, @as(json`[]`) _) => unit = - "useLayoutEffect" +external useLayoutEffect0: (unit => option unit>, %raw("[]")) => unit = "useLayoutEffect" @module("react") external useLayoutEffect1: (unit => option unit>, array<'a>) => unit = "useLayoutEffect" @module("react") @@ -169,7 +168,7 @@ external useLayoutEffect7: (unit => option unit>, ('a, 'b, 'c, 'd, 'e, ' @module("react") external useMemo: (unit => 'any) => 'any = "useMemo" @module("react") -external useMemo0: (unit => 'any, @as(json`[]`) _) => 'any = "useMemo" +external useMemo0: (unit => 'any, %raw("[]")) => 'any = "useMemo" @module("react") external useMemo1: (unit => 'any, array<'a>) => 'any = "useMemo" @module("react") @@ -191,8 +190,7 @@ type callback<'input, 'output> = 'input => 'output @module("react") external useCallback: ('input => 'output) => callback<'input, 'output> = "useCallback" @module("react") -external useCallback0: ('input => 'output, @as(json`[]`) _) => callback<'input, 'output> = - "useCallback" +external useCallback0: ('input => 'output, %raw("[]")) => callback<'input, 'output> = "useCallback" @module("react") external useCallback1: ('input => 'output, array<'a>) => callback<'input, 'output> = "useCallback" @module("react") @@ -221,7 +219,7 @@ external useContext: Context.t<'any> => 'any = "useContext" @module("react") external useRef: 'value => Ref.t<'value> = "useRef" @module("react") -external useImperativeHandle0: (nullable>, unit => 'value, @as(json`[]`) _) => unit = +external useImperativeHandle0: (nullable>, unit => 'value, %raw("[]")) => unit = "useImperativeHandle" @module("react") diff --git a/tests/build_tests/super_errors/expected/raw_external_arg_not_literal.res.expected b/tests/build_tests/super_errors/expected/raw_external_arg_not_literal.res.expected new file mode 100644 index 00000000000..375f6706b3a --- /dev/null +++ b/tests/build_tests/super_errors/expected/raw_external_arg_not_literal.res.expected @@ -0,0 +1,8 @@ + + We've found a bug for you! + /.../fixtures/raw_external_arg_not_literal.res:1:24-37 + + 1 │ @val external f: (%raw("1) + alert(2"), int) => unit = "f" + 2 │ + + The %raw payload must be a JavaScript literal \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/raw_external_arg_optional.res.expected b/tests/build_tests/super_errors/expected/raw_external_arg_optional.res.expected new file mode 100644 index 00000000000..373e17a05e8 --- /dev/null +++ b/tests/build_tests/super_errors/expected/raw_external_arg_optional.res.expected @@ -0,0 +1,8 @@ + + We've found a bug for you! + /.../fixtures/raw_external_arg_optional.res:1:23-31 + + 1 │ @val external f: (~x: %raw("1")=?, unit) => unit = "f" + 2 │ + + A %raw argument cannot be optional \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/raw_external_arg_unused_attribute.res.expected b/tests/build_tests/super_errors/expected/raw_external_arg_unused_attribute.res.expected new file mode 100644 index 00000000000..040fce3e14f --- /dev/null +++ b/tests/build_tests/super_errors/expected/raw_external_arg_unused_attribute.res.expected @@ -0,0 +1,10 @@ + + Warning number 101 (configured as error) + /.../fixtures/raw_external_arg_unused_attribute.res:1:14-20 + + 1 │ external f: (@ignore %raw("1"), int) => int = "f" + 2 │ + + Unused attribute: @ignore +This attribute has no effect here. +For example, some attributes are only meaningful in externals. \ No newline at end of file diff --git a/tests/build_tests/super_errors/fixtures/raw_external_arg_not_literal.res b/tests/build_tests/super_errors/fixtures/raw_external_arg_not_literal.res new file mode 100644 index 00000000000..35553e9cbdc --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/raw_external_arg_not_literal.res @@ -0,0 +1 @@ +@val external f: (%raw("1) + alert(2"), int) => unit = "f" diff --git a/tests/build_tests/super_errors/fixtures/raw_external_arg_optional.res b/tests/build_tests/super_errors/fixtures/raw_external_arg_optional.res new file mode 100644 index 00000000000..b692130c695 --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/raw_external_arg_optional.res @@ -0,0 +1 @@ +@val external f: (~x: %raw("1")=?, unit) => unit = "f" diff --git a/tests/build_tests/super_errors/fixtures/raw_external_arg_unused_attribute.res b/tests/build_tests/super_errors/fixtures/raw_external_arg_unused_attribute.res new file mode 100644 index 00000000000..2d801e8c350 --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/raw_external_arg_unused_attribute.res @@ -0,0 +1 @@ +external f: (@ignore %raw("1"), int) => int = "f" diff --git a/tests/dependencies/rescript-react/src/React.res b/tests/dependencies/rescript-react/src/React.res index ff2b1cd4d58..2b81b8d8b84 100644 --- a/tests/dependencies/rescript-react/src/React.res +++ b/tests/dependencies/rescript-react/src/React.res @@ -150,7 +150,7 @@ external useEffectOnEveryRender: (unit => option unit>) => unit = "useEf @module("react") external useEffect: (unit => option unit>, 'deps) => unit = "useEffect" @module("react") -external useEffect0: (unit => option unit>, @as(json`[]`) _) => unit = "useEffect" +external useEffect0: (unit => option unit>, %raw("[]")) => unit = "useEffect" @module("react") external useEffect1: (unit => option unit>, array<'a>) => unit = "useEffect" @module("react") @@ -172,8 +172,7 @@ external useLayoutEffectOnEveryRender: (unit => option unit>) => unit = @module("react") external useLayoutEffect: (unit => option unit>, 'deps) => unit = "useLayoutEffect" @module("react") -external useLayoutEffect0: (unit => option unit>, @as(json`[]`) _) => unit = - "useLayoutEffect" +external useLayoutEffect0: (unit => option unit>, %raw("[]")) => unit = "useLayoutEffect" @module("react") external useLayoutEffect1: (unit => option unit>, array<'a>) => unit = "useLayoutEffect" @module("react") @@ -197,7 +196,7 @@ external useLayoutEffect7: (unit => option unit>, ('a, 'b, 'c, 'd, 'e, ' external useMemo: (unit => 'any, 'deps) => 'any = "useMemo" @module("react") -external useMemo0: (unit => 'any, @as(json`[]`) _) => 'any = "useMemo" +external useMemo0: (unit => 'any, %raw("[]")) => 'any = "useMemo" @module("react") external useMemo1: (unit => 'any, array<'a>) => 'any = "useMemo" @@ -224,7 +223,7 @@ external useMemo7: (unit => 'any, ('a, 'b, 'c, 'd, 'e, 'f, 'g)) => 'any = "useMe external useCallback: ('f, 'deps) => 'f = "useCallback" @module("react") -external useCallback0: ('f, @as(json`[]`) _) => 'f = "useCallback" +external useCallback0: ('f, %raw("[]")) => 'f = "useCallback" @module("react") external useCallback1: ('f, array<'a>) => 'f = "useCallback" @@ -264,7 +263,7 @@ external useImperativeHandle: (nullable>, unit => 'value, 'deps) => "useImperativeHandle" @module("react") -external useImperativeHandle0: (nullable>, unit => 'value, @as(json`[]`) _) => unit = +external useImperativeHandle0: (nullable>, unit => 'value, %raw("[]")) => unit = "useImperativeHandle" @module("react") @@ -316,7 +315,7 @@ external useInsertionEffectOnEveryRender: (unit => option unit>) => unit @module("react") external useInsertionEffect: (unit => option unit>, 'deps) => unit = "useInsertionEffect" @module("react") -external useInsertionEffect0: (unit => option unit>, @as(json`[]`) _) => unit = +external useInsertionEffect0: (unit => option unit>, %raw("[]")) => unit = "useInsertionEffect" @module("react") external useInsertionEffect1: (unit => option unit>, array<'a>) => unit = @@ -372,7 +371,7 @@ module Uncurried = { external useCallback: ('f, 'deps) => 'f = "useCallback" @module("react") - external useCallback0: ('f, @as(json`[]`) _) => 'f = "useCallback" + external useCallback0: ('f, %raw("[]")) => 'f = "useCallback" @module("react") external useCallback1: ('f, array<'a>) => 'f = "useCallback" diff --git a/tests/dependencies/rescript-react/src/ReactDOMStyle.res b/tests/dependencies/rescript-react/src/ReactDOMStyle.res index f9ead0b0ab4..6894811ec4e 100644 --- a/tests/dependencies/rescript-react/src/ReactDOMStyle.res +++ b/tests/dependencies/rescript-react/src/ReactDOMStyle.res @@ -2,7 +2,7 @@ type t = JsxDOMStyle.t /* CSS2Properties: https://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSS2Properties */ @val -external combine: (@as(json`{}`) _, t, t) => t = "Object.assign" +external combine: (%raw("{}"), t, t) => t = "Object.assign" external _dictToStyle: dict => t = "%identity" @@ -13,4 +13,4 @@ let unsafeAddProp = (style, key, value) => { } @val -external unsafeAddStyle: (@as(json`{}`) _, t, {..}) => t = "Object.assign" +external unsafeAddStyle: (%raw("{}"), t, {..}) => t = "Object.assign" diff --git a/tests/dependencies/rescript-react/src/RescriptReactRouter.res b/tests/dependencies/rescript-react/src/RescriptReactRouter.res index 747154149db..2087a0fb9b7 100644 --- a/tests/dependencies/rescript-react/src/RescriptReactRouter.res +++ b/tests/dependencies/rescript-react/src/RescriptReactRouter.res @@ -23,10 +23,10 @@ external dispatchEvent: (Dom.window, Dom.event) => unit = "dispatchEvent" @get external search: Dom.location => string = "search" @send -external pushState: (Dom.history, @as(json`null`) _, @as("") _, ~href: string) => unit = "pushState" +external pushState: (Dom.history, %raw("null"), %raw(`""`), ~href: string) => unit = "pushState" @send -external replaceState: (Dom.history, @as(json`null`) _, @as("") _, ~href: string) => unit = +external replaceState: (Dom.history, %raw("null"), %raw(`""`), ~href: string) => unit = "replaceState" type makeEventOptions = { diff --git a/tests/ounit_tests/ounit_ast_mapper0_tests.ml b/tests/ounit_tests/ounit_ast_mapper0_tests.ml index c20a3e50eec..4c71f127565 100644 --- a/tests/ounit_tests/ounit_ast_mapper0_tests.ml +++ b/tests/ounit_tests/ounit_ast_mapper0_tests.ml @@ -341,6 +341,8 @@ let test_fixed_argument_locations_go_through_the_mapper _ = match value.txt with | Pfixed_int source | Pfixed_template source | Pfixed_json source -> OUnit.assert_equal marker source.loc + | Pfixed_raw source -> + OUnit.assert_equal marker (Ast_raw.source_loc source) | Pfixed_string _ -> ()) [(json.loc, json.value); (int.loc, int.value)] | _ -> assert_failure "Expected the fixed arguments back"); @@ -374,6 +376,47 @@ let test_fixed_argument_locations_go_through_the_mapper _ = assert_relocated_v0_fixed int0 | _ -> assert_failure "Expected an external arrow on the ast0 wire" +let test_raw_fixed_argument_survives_ast0 _ = + (* v0 has no [%raw]: its source travels as a [json] payload and comes back as + a fixed argument with the same JavaScript source. *) + let int_type = + Ast_helper.Typ.constr ~loc (Location.mknoloc (Longident.Lident "int")) [] + in + let vd = + Ast_helper.Val.mk ~prim:(Parsetree.Prim_name "f") (Location.mknoloc "f") + (Ast_helper.Typ.arrow ~loc + [ + Parg_fixed + { + attrs = []; + lbl = Nolabel; + value = + { + txt = + Pfixed_raw (Raw_string {txt = "{}"; loc = source_loc 6 8}); + loc = source_loc 6 8; + }; + loc = source_loc 1 9; + }; + Parg_type {attrs = []; lbl = Nolabel; typ = int_type}; + ] + int_type) + in + let vd' = + Ast_mapper_from0.default_mapper.value_description + Ast_mapper_from0.default_mapper + (Ast_mapper_to0.default_mapper.value_description + Ast_mapper_to0.default_mapper vd) + in + match vd'.pval_type.ptyp_desc with + | Ptyp_arrow {params = [Parg_fixed {value; loc}; Parg_type _]} -> + OUnit.assert_equal + ~printer:(fun s -> s) + "{}" + (Ast_fixed_arg.js_source value.txt); + OUnit.assert_equal (source_loc 1 9) loc + | _ -> assert_failure "Expected the %raw argument back as a fixed argument" + let test_fixed_argument_is_external_only _ = (* A non-external arrow keeps [@as(1) _] as an ordinary argument. *) let any_as = @@ -2510,4 +2553,6 @@ let suites = >:: test_fixed_argument_is_external_only; "fixed_argument_locations_go_through_the_mapper" >:: test_fixed_argument_locations_go_through_the_mapper; + "raw_fixed_argument_survives_ast0" + >:: test_raw_fixed_argument_survives_ast0; ] diff --git a/tests/ounit_tests/ounit_flow_parser_tests.ml b/tests/ounit_tests/ounit_flow_parser_tests.ml index c1a8f07745f..94f811e4e91 100644 --- a/tests/ounit_tests/ounit_flow_parser_tests.ml +++ b/tests/ounit_tests/ounit_flow_parser_tests.ml @@ -50,4 +50,54 @@ let suites = (Classify_function.classify_stmt "// only a comment"); OUnit.assert_equal Js_raw_info.Js_stmt_unknown (Classify_function.classify_stmt "console.log('hello')") ); + ( "fixed literals are exactly one literal" >:: fun _ -> + List.iter + (fun source -> + OUnit.assert_bool source + (Classify_function.is_fixed_literal source)) + [ + "1"; + "-1"; + "-1n"; + "-1.5"; + "{a: -2}"; + "1n"; + {|"s"|}; + "{}"; + "0o7"; + "0"; + {|"\0"|}; + "{a: [1, null]}"; + "/a/g"; + "undefined"; + "true"; + {|{"a": 1, 2: 3, b: [4]}|}; + ]; + List.iter + (fun source -> + OUnit.assert_bool source + (not (Classify_function.is_fixed_literal source))) + [ + "007"; + {|"\07"|}; + "{[f()]: 0}"; + {|{["a"]: 0}|}; + "{a: {[f()]: 0}}"; + "-[]"; + "{x: -{}}"; + "-/a/"; + {|-"1"|}; + "-true"; + "-(-1)"; + "1 2"; + "1) + alert(2"; + "1; alert(2)"; + "1, 2"; + "1 // comment"; + "/* comment */ 1"; + "x"; + "f()"; + "`t`"; + ""; + ] ); ] diff --git a/tests/ounit_tests/ounit_string_literal_tests.ml b/tests/ounit_tests/ounit_string_literal_tests.ml index a4a879cbd0d..25e7e47f55d 100644 --- a/tests/ounit_tests/ounit_string_literal_tests.ml +++ b/tests/ounit_tests/ounit_string_literal_tests.ml @@ -570,6 +570,40 @@ let suites = with | _ -> OUnit.assert_failure "expected an invalid string escape" | exception Location.Error _ -> () ); + ( "fixed-value payloads become JavaScript sources" >:: fun _ -> + let js_source payload = + Option.map + (fun (value : Parsetree.fixed_value Location.loc) -> + Ast_fixed_arg.js_source value.txt) + (Ast_fixed_arg.of_payload payload) + in + let int_payload source = + Parsetree.PStr + [ + Ast_helper.Str.eval + (Ast_helper.Exp.constant (Pconst_integer (source, None))); + ] + in + List.iter + (fun (source, expected) -> + OUnit.assert_equal ~printer:Ext_obj.dump (Some expected) + (js_source (int_payload source))) + [ + ("1", "1"); + ("0", "0"); + ("00", "0"); + ("007", "7"); + ("-007", "-7"); + ("1_000", "1000"); + ("0x1_0", "0x10"); + ("0o17", "0o17"); + ]; + OUnit.assert_equal ~printer:Ext_obj.dump (Some {|"a\n😀"|}) + (js_source (template_payload {|a\n\uD83D\uDE00|})); + OUnit.assert_equal ~printer:Ext_obj.dump (Some {|{"answer":42}|}) + (js_source (tagged_payload "json" {|{"answer":42}|})); + OUnit.assert_equal ~printer:Ext_obj.dump None + (js_source (tagged_payload "sql" {|{"answer":42}|})) ); ( "error extensions accept backquoted messages" >:: fun _ -> let extension = ( Location.mknoloc "error", diff --git a/tests/syntax_tests/data/ast-mapping/FixedExternal.res b/tests/syntax_tests/data/ast-mapping/FixedExternal.res index 9c9a31b3f06..93ea772752b 100644 --- a/tests/syntax_tests/data/ast-mapping/FixedExternal.res +++ b/tests/syntax_tests/data/ast-mapping/FixedExternal.res @@ -3,3 +3,4 @@ @val external labelled: (~mode: @as(1) _, ~other: @as("o") _, int) => int = "f" @obj external obj: (~a: @as(1) _, ~b: int, unit) => _ = "" @val external single: @as(-1) _ => int = "f" +@val external raw: (%raw("{}"), ~mode: %raw(`"img"`), int) => int = "f" diff --git a/tests/syntax_tests/data/ast-mapping/expected/FixedExternal.res.txt b/tests/syntax_tests/data/ast-mapping/expected/FixedExternal.res.txt index 9c9a31b3f06..51f0cf68620 100644 --- a/tests/syntax_tests/data/ast-mapping/expected/FixedExternal.res.txt +++ b/tests/syntax_tests/data/ast-mapping/expected/FixedExternal.res.txt @@ -1,5 +1,93 @@ -@val external json: (@as(json`{"a": [1, 2]}`) _, int) => int = "f" -@val external strings: (@as("s") _, @as(`t`) _, int) => int = "f" -@val external labelled: (~mode: @as(1) _, ~other: @as("o") _, int) => int = "f" -@obj external obj: (~a: @as(1) _, ~b: int, unit) => _ = "" -@val external single: @as(-1) _ => int = "f" + + Warning number 3 + syntax_tests/data/ast-mapping/FixedExternal.res:1:22-47 + + 1 │ @val external json: (@as(json`{"a": [1, 2]}`) _, int) => int = "f" + 2 │ @val external strings: (@as("s") _, @as(`t`) _, int) => int = "f" + 3 │ @val external labelled: (~mode: @as(1) _, ~other: @as("o") _, int) => in + │ t = "f" + + deprecated: The fixed-value `@as(...) _` external argument syntax is deprecated. Use `%raw(...)` instead. + + + Warning number 3 + syntax_tests/data/ast-mapping/FixedExternal.res:2:25-34 + + 1 │ @val external json: (@as(json`{"a": [1, 2]}`) _, int) => int = "f" + 2 │ @val external strings: (@as("s") _, @as(`t`) _, int) => int = "f" + 3 │ @val external labelled: (~mode: @as(1) _, ~other: @as("o") _, int) => in + │ t = "f" + 4 │ @obj external obj: (~a: @as(1) _, ~b: int, unit) => _ = "" + + deprecated: The fixed-value `@as(...) _` external argument syntax is deprecated. Use `%raw(...)` instead. + + + Warning number 3 + syntax_tests/data/ast-mapping/FixedExternal.res:2:37-46 + + 1 │ @val external json: (@as(json`{"a": [1, 2]}`) _, int) => int = "f" + 2 │ @val external strings: (@as("s") _, @as(`t`) _, int) => int = "f" + 3 │ @val external labelled: (~mode: @as(1) _, ~other: @as("o") _, int) => in + │ t = "f" + 4 │ @obj external obj: (~a: @as(1) _, ~b: int, unit) => _ = "" + + deprecated: The fixed-value `@as(...) _` external argument syntax is deprecated. Use `%raw(...)` instead. + + + Warning number 3 + syntax_tests/data/ast-mapping/FixedExternal.res:3:33-40 + + 1 │ @val external json: (@as(json`{"a": [1, 2]}`) _, int) => int = "f" + 2 │ @val external strings: (@as("s") _, @as(`t`) _, int) => int = "f" + 3 │ @val external labelled: (~mode: @as(1) _, ~other: @as("o") _, int) => in + │ t = "f" + 4 │ @obj external obj: (~a: @as(1) _, ~b: int, unit) => _ = "" + 5 │ @val external single: @as(-1) _ => int = "f" + + deprecated: The fixed-value `@as(...) _` external argument syntax is deprecated. Use `%raw(...)` instead. + + + Warning number 3 + syntax_tests/data/ast-mapping/FixedExternal.res:3:51-60 + + 1 │ @val external json: (@as(json`{"a": [1, 2]}`) _, int) => int = "f" + 2 │ @val external strings: (@as("s") _, @as(`t`) _, int) => int = "f" + 3 │ @val external labelled: (~mode: @as(1) _, ~other: @as("o") _, int) => in + │ t = "f" + 4 │ @obj external obj: (~a: @as(1) _, ~b: int, unit) => _ = "" + 5 │ @val external single: @as(-1) _ => int = "f" + + deprecated: The fixed-value `@as(...) _` external argument syntax is deprecated. Use `%raw(...)` instead. + + + Warning number 3 + syntax_tests/data/ast-mapping/FixedExternal.res:4:25-32 + + 2 │ @val external strings: (@as("s") _, @as(`t`) _, int) => int = "f" + 3 │ @val external labelled: (~mode: @as(1) _, ~other: @as("o") _, int) => in + │ t = "f" + 4 │ @obj external obj: (~a: @as(1) _, ~b: int, unit) => _ = "" + 5 │ @val external single: @as(-1) _ => int = "f" + 6 │ @val external raw: (%raw("{}"), ~mode: %raw(`"img"`), int) => int = "f" + + deprecated: The fixed-value `@as(...) _` external argument syntax is deprecated. Use `%raw(...)` instead. + + + Warning number 3 + syntax_tests/data/ast-mapping/FixedExternal.res:5:23-31 + + 3 │ @val external labelled: (~mode: @as(1) _, ~other: @as("o") _, int) => in + │ t = "f" + 4 │ @obj external obj: (~a: @as(1) _, ~b: int, unit) => _ = "" + 5 │ @val external single: @as(-1) _ => int = "f" + 6 │ @val external raw: (%raw("{}"), ~mode: %raw(`"img"`), int) => int = "f" + 7 │ + + deprecated: The fixed-value `@as(...) _` external argument syntax is deprecated. Use `%raw(...)` instead. + +@val external json: (%raw(`{"a": [1, 2]}`), int) => int = "f" +@val external strings: (%raw(`"s"`), %raw(`"t"`), int) => int = "f" +@val external labelled: (~mode: %raw("1"), ~other: %raw(`"o"`), int) => int = "f" +@obj external obj: (~a: %raw("1"), ~b: int, unit) => _ = "" +@val external single: (%raw("-1")) => int = "f" +@val external raw: (%raw("{}"), ~mode: %raw(`"img"`), int) => int = "f" diff --git a/tests/syntax_tests/data/ast-mapping/expected/FunctionsAndArrows.res.txt b/tests/syntax_tests/data/ast-mapping/expected/FunctionsAndArrows.res.txt index 508c2860fb2..a6f4ef9b100 100644 --- a/tests/syntax_tests/data/ast-mapping/expected/FunctionsAndArrows.res.txt +++ b/tests/syntax_tests/data/ast-mapping/expected/FunctionsAndArrows.res.txt @@ -1,3 +1,16 @@ + + Warning number 3 + syntax_tests/data/ast-mapping/FunctionsAndArrows.res:47:29-46 + + 45 │ // phantom @as arguments: written arity differs from lowered call arity + 46 │ @val + 47 │ external phantom: (~a: int, @as(json`false`) _, ~c: string) => unit = " + │ phantom" + 48 │ + 49 │ // external with uncurried callback argument + + deprecated: The fixed-value `@as(...) _` external argument syntax is deprecated. Use `%raw(...)` instead. + // Round-trip coverage for functions and arrow types through the // Parsetree0 bridge (ast_mapper_to0 / ast_mapper_from0). @@ -44,7 +57,7 @@ type argAttr = (@as("x") ~foo: string, int) => int // phantom @as arguments: written arity differs from lowered call arity @val -external phantom: (~a: int, @as(json`false`) _, ~c: string) => unit = "phantom" +external phantom: (~a: int, %raw("false"), ~c: string) => unit = "phantom" // external with uncurried callback argument @val external onEvent: (string, (~event: string) => unit) => unit = "on" diff --git a/tests/syntax_tests/data/parsing/errors/typexpr/expected/fixedExternalDuplicate.res.txt b/tests/syntax_tests/data/parsing/errors/typexpr/expected/fixedExternalDuplicate.res.txt new file mode 100644 index 00000000000..86cb5ae9a6d --- /dev/null +++ b/tests/syntax_tests/data/parsing/errors/typexpr/expected/fixedExternalDuplicate.res.txt @@ -0,0 +1,10 @@ + + Syntax error! + syntax_tests/data/parsing/errors/typexpr/fixedExternalDuplicate.res:1:31-32 + + 1 │ external duplicate: (%raw("1") %raw("2")) => unit = "duplicate" + 2 │ + + Did you forget a `,` here? + +external duplicate : %raw("1") -> %raw("2") -> unit (a:2) = "duplicate" \ No newline at end of file diff --git a/tests/syntax_tests/data/parsing/errors/typexpr/expected/fixedExternalPosition.res.txt b/tests/syntax_tests/data/parsing/errors/typexpr/expected/fixedExternalPosition.res.txt new file mode 100644 index 00000000000..def2d21233a --- /dev/null +++ b/tests/syntax_tests/data/parsing/errors/typexpr/expected/fixedExternalPosition.res.txt @@ -0,0 +1,11 @@ + + Syntax error! + syntax_tests/data/parsing/errors/typexpr/fixedExternalPosition.res:1:28-31 + + 1 │ external nestedArgument: ((%raw("1")) => int) => unit = "nestedArgument" + 2 │ + + %raw is only allowed as a direct external function argument + +external nestedArgument : + ([%raw {js|1|js}] -> int (a:1)) -> unit (a:1) = "nestedArgument" \ No newline at end of file diff --git a/tests/syntax_tests/data/parsing/errors/typexpr/expected/fixedExternalTyped.res.txt b/tests/syntax_tests/data/parsing/errors/typexpr/expected/fixedExternalTyped.res.txt new file mode 100644 index 00000000000..967da7f1a40 --- /dev/null +++ b/tests/syntax_tests/data/parsing/errors/typexpr/expected/fixedExternalTyped.res.txt @@ -0,0 +1,10 @@ + + Syntax error! + syntax_tests/data/parsing/errors/typexpr/fixedExternalTyped.res:1:26-29 + + 1 │ external typedArgument: (%raw(1), int) => unit = "typedArgument" + 2 │ + + The %raw extension can only be applied to a string + +external typedArgument : [%raw 1] -> int -> unit (a:2) = "typedArgument" \ No newline at end of file diff --git a/tests/syntax_tests/data/parsing/errors/typexpr/fixedExternalDuplicate.res b/tests/syntax_tests/data/parsing/errors/typexpr/fixedExternalDuplicate.res new file mode 100644 index 00000000000..a4fb03d4c25 --- /dev/null +++ b/tests/syntax_tests/data/parsing/errors/typexpr/fixedExternalDuplicate.res @@ -0,0 +1 @@ +external duplicate: (%raw("1") %raw("2")) => unit = "duplicate" diff --git a/tests/syntax_tests/data/parsing/errors/typexpr/fixedExternalPosition.res b/tests/syntax_tests/data/parsing/errors/typexpr/fixedExternalPosition.res new file mode 100644 index 00000000000..6c3d1f50dad --- /dev/null +++ b/tests/syntax_tests/data/parsing/errors/typexpr/fixedExternalPosition.res @@ -0,0 +1 @@ +external nestedArgument: ((%raw("1")) => int) => unit = "nestedArgument" diff --git a/tests/syntax_tests/data/parsing/errors/typexpr/fixedExternalTyped.res b/tests/syntax_tests/data/parsing/errors/typexpr/fixedExternalTyped.res new file mode 100644 index 00000000000..8e5f9a83f71 --- /dev/null +++ b/tests/syntax_tests/data/parsing/errors/typexpr/fixedExternalTyped.res @@ -0,0 +1 @@ +external typedArgument: (%raw(1), int) => unit = "typedArgument" diff --git a/tests/syntax_tests/data/printer/other/expected/attributes.res.txt b/tests/syntax_tests/data/printer/other/expected/attributes.res.txt index b34daf6830c..ad36ee21b69 100644 --- a/tests/syntax_tests/data/printer/other/expected/attributes.res.txt +++ b/tests/syntax_tests/data/printer/other/expected/attributes.res.txt @@ -1,3 +1,75 @@ + + Warning number 3 + syntax_tests/data/printer/other/attributes.res:29:42-49 + + 27 │ @obj external ff: (~x: int, ~g: int, ~h: int) => _ = "" + 28 │ + 29 │ @obj external ff: (~x: int, ~g: int, ~h: @as(3) _) => _ = "" + 30 │ + 31 │ @obj external ff: (~x: int) => (~h: @as(3) _) => _ = "" + + deprecated: The fixed-value `@as(...) _` external argument syntax is deprecated. Use `%raw(...)` instead. + + + Warning number 3 + syntax_tests/data/printer/other/attributes.res:35:8-15 + + 33 │ @obj + 34 │ external ff: ( + 35 │ ~lo: @as(3) _, + 36 │ ~mid: @as(3) _, + 37 │ ~hi: int, + + deprecated: The fixed-value `@as(...) _` external argument syntax is deprecated. Use `%raw(...)` instead. + + + Warning number 3 + syntax_tests/data/printer/other/attributes.res:36:9-16 + + 34 │ external ff: ( + 35 │ ~lo: @as(3) _, + 36 │ ~mid: @as(3) _, + 37 │ ~hi: int, + 38 │ ) => _ = "" + + deprecated: The fixed-value `@as(...) _` external argument syntax is deprecated. Use `%raw(...)` instead. + + + Warning number 3 + syntax_tests/data/printer/other/attributes.res:44:8-15 + + 42 │ external ff: ( + 43 │ ~hi: int, + 44 │ ~lo: @as(3) _ + 45 │ ) => _ = "" + 46 │ + + deprecated: The fixed-value `@as(...) _` external argument syntax is deprecated. Use `%raw(...)` instead. + + + Warning number 3 + syntax_tests/data/printer/other/attributes.res:50:9-16 + + 48 │ external ff: ( + 49 │ ~hi: int, + 50 │ ~mid: @as(3) _, + 51 │ ~lo: @as(3) _ + 52 │ ) => _ = "" + + deprecated: The fixed-value `@as(...) _` external argument syntax is deprecated. Use `%raw(...)` instead. + + + Warning number 3 + syntax_tests/data/printer/other/attributes.res:51:8-15 + + 49 │ ~hi: int, + 50 │ ~mid: @as(3) _, + 51 │ ~lo: @as(3) _ + 52 │ ) => _ = "" + 53 │ + + deprecated: The fixed-value `@as(...) _` external argument syntax is deprecated. Use `%raw(...)` instead. + @attr(: int) let x = 1 @@ -25,15 +97,15 @@ let x = 1 @obj external ff: (~x: int, ~g: int, ~h: int) => _ = "" -@obj external ff: (~x: int, ~g: int, ~h: @as(3) _) => _ = "" +@obj external ff: (~x: int, ~g: int, ~h: %raw("3")) => _ = "" @obj external ff: (~x: int) => (~h: @as(3) _) => _ = "" @obj -external ff: (~lo: @as(3) _, ~mid: @as(3) _, ~hi: int) => _ = "" +external ff: (~lo: %raw("3"), ~mid: %raw("3"), ~hi: int) => _ = "" @obj -external ff: (~hi: int, ~lo: @as(3) _) => _ = "" +external ff: (~hi: int, ~lo: %raw("3")) => _ = "" @obj -external ff: (~hi: int, ~mid: @as(3) _, ~lo: @as(3) _) => _ = "" +external ff: (~hi: int, ~mid: %raw("3"), ~lo: %raw("3")) => _ = "" diff --git a/tests/syntax_tests/data/printer/typexpr/expected/fixedExternal.res.txt b/tests/syntax_tests/data/printer/typexpr/expected/fixedExternal.res.txt index 0f6440d64d7..eb15e7b14da 100644 --- a/tests/syntax_tests/data/printer/typexpr/expected/fixedExternal.res.txt +++ b/tests/syntax_tests/data/printer/typexpr/expected/fixedExternal.res.txt @@ -1,30 +1,300 @@ + + Warning number 3 + syntax_tests/data/printer/typexpr/fixedExternal.res:2:27-41 + + 1 │ @val + 2 │ external unsafeAddStyle: (@as(json`{}`) _, t, {..}) => t = "Object.assig + │ n" + 3 │ + 4 │ @module("react") + + deprecated: The fixed-value `@as(...) _` external argument syntax is deprecated. Use `%raw(...)` instead. + + + Warning number 3 + syntax_tests/data/printer/typexpr/fixedExternal.res:5:53-67 + + 3 │ + 4 │ @module("react") + 5 │ external useEffect0: (unit => option unit>, @as(json`[]`) _) => + │ unit = "useEffect" + 6 │ + 7 │ @send external createElement: (document, @as("img") _) => element = "cre + │ ateElement" + + deprecated: The fixed-value `@as(...) _` external argument syntax is deprecated. Use `%raw(...)` instead. + + + Warning number 3 + syntax_tests/data/printer/typexpr/fixedExternal.res:7:42-53 + + 5 │ external useEffect0: (unit => option unit>, @as(json`[]`) _) => + │ unit = "useEffect" + 6 │ + 7 │ @send external createElement: (document, @as("img") _) => element = "cre + │ ateElement" + 8 │ + 9 │ @val external templateString: (@as(`ab`) _, int) => int = "f" + + deprecated: The fixed-value `@as(...) _` external argument syntax is deprecated. Use `%raw(...)` instead. + + + Warning number 3 + syntax_tests/data/printer/typexpr/fixedExternal.res:9:32-42 + + 7 │ @send external createElement: (document, @as("img") _) => element = "cr + │ eateElement" + 8 │ + 9 │ @val external templateString: (@as(`ab`) _, int) => int = "f" + 10 │ + 11 │ @val external numbers: (@as(1) _, @as(-1) _, @as(0x10) _, int) => int = + │ "f" + + deprecated: The fixed-value `@as(...) _` external argument syntax is deprecated. Use `%raw(...)` instead. + + + Warning number 3 + syntax_tests/data/printer/typexpr/fixedExternal.res:11:25-32 + + 9 │ @val external templateString: (@as(`ab`) _, int) => int = "f" + 10 │ + 11 │ @val external numbers: (@as(1) _, @as(-1) _, @as(0x10) _, int) => int = + │ "f" + 12 │ + 13 │ @obj external makeOptions: (~objectMode: @as(false) _, ~name: string, ~ + │ mode: @as("fast") _, unit) => options = "" + + deprecated: The fixed-value `@as(...) _` external argument syntax is deprecated. Use `%raw(...)` instead. + + + Warning number 3 + syntax_tests/data/printer/typexpr/fixedExternal.res:11:35-43 + + 9 │ @val external templateString: (@as(`ab`) _, int) => int = "f" + 10 │ + 11 │ @val external numbers: (@as(1) _, @as(-1) _, @as(0x10) _, int) => int = + │ "f" + 12 │ + 13 │ @obj external makeOptions: (~objectMode: @as(false) _, ~name: string, ~ + │ mode: @as("fast") _, unit) => options = "" + + deprecated: The fixed-value `@as(...) _` external argument syntax is deprecated. Use `%raw(...)` instead. + + + Warning number 3 + syntax_tests/data/printer/typexpr/fixedExternal.res:11:46-56 + + 9 │ @val external templateString: (@as(`ab`) _, int) => int = "f" + 10 │ + 11 │ @val external numbers: (@as(1) _, @as(-1) _, @as(0x10) _, int) => int = + │ "f" + 12 │ + 13 │ @obj external makeOptions: (~objectMode: @as(false) _, ~name: string, ~ + │ mode: @as("fast") _, unit) => options = "" + + deprecated: The fixed-value `@as(...) _` external argument syntax is deprecated. Use `%raw(...)` instead. + + + Warning number 3 + syntax_tests/data/printer/typexpr/fixedExternal.res:13:78-90 + + 11 │ @val external numbers: (@as(1) _, @as(-1) _, @as(0x10) _, int) => int = + │ "f" + 12 │ + 13 │ @obj external makeOptions: (~objectMode: @as(false) _, ~name: string, ~ + │ mode: @as("fast") _, unit) => options = "" + 14 │ + 15 │ @val external single: @as(1) _ => int = "f" + + deprecated: The fixed-value `@as(...) _` external argument syntax is deprecated. Use `%raw(...)` instead. + + + Warning number 3 + syntax_tests/data/printer/typexpr/fixedExternal.res:15:23-30 + + 13 │ @obj external makeOptions: (~objectMode: @as(false) _, ~name: string, ~ + │ mode: @as("fast") _, unit) => options = "" + 14 │ + 15 │ @val external single: @as(1) _ => int = "f" + 16 │ + 17 │ @val external attributes: (@foo @as(1) _, @as(1) @foo _, @as({1}) _, in + │ t) => int = "f" + + deprecated: The fixed-value `@as(...) _` external argument syntax is deprecated. Use `%raw(...)` instead. + + + Warning number 3 + syntax_tests/data/printer/typexpr/fixedExternal.res:17:33-40 + + 15 │ @val external single: @as(1) _ => int = "f" + 16 │ + 17 │ @val external attributes: (@foo @as(1) _, @as(1) @foo _, @as({1}) _, in + │ t) => int = "f" + 18 │ + 19 │ @val + + deprecated: The fixed-value `@as(...) _` external argument syntax is deprecated. Use `%raw(...)` instead. + + + Warning number 3 + syntax_tests/data/printer/typexpr/fixedExternal.res:17:43-55 + + 15 │ @val external single: @as(1) _ => int = "f" + 16 │ + 17 │ @val external attributes: (@foo @as(1) _, @as(1) @foo _, @as({1}) _, in + │ t) => int = "f" + 18 │ + 19 │ @val + + deprecated: The fixed-value `@as(...) _` external argument syntax is deprecated. Use `%raw(...)` instead. + + + Warning number 3 + syntax_tests/data/printer/typexpr/fixedExternal.res:17:58-67 + + 15 │ @val external single: @as(1) _ => int = "f" + 16 │ + 17 │ @val external attributes: (@foo @as(1) _, @as(1) @foo _, @as({1}) _, in + │ t) => int = "f" + 18 │ + 19 │ @val + + deprecated: The fixed-value `@as(...) _` external argument syntax is deprecated. Use `%raw(...)` instead. + + + Warning number 3 + syntax_tests/data/printer/typexpr/fixedExternal.res:20:30-45 + + 18 │ + 19 │ @val + 20 │ external commented: (/* a */ @as(1) /* b */ _ /* c */, @as(/* d */ "s") + │ _, int) => int = "f" + 21 │ + 22 │ type notExternal = (@as(1) _, int) => int + + deprecated: The fixed-value `@as(...) _` external argument syntax is deprecated. Use `%raw(...)` instead. + + + Warning number 3 + syntax_tests/data/printer/typexpr/fixedExternal.res:20:56-73 + + 18 │ + 19 │ @val + 20 │ external commented: (/* a */ @as(1) /* b */ _ /* c */, @as(/* d */ "s") + │ _, int) => int = "f" + 21 │ + 22 │ type notExternal = (@as(1) _, int) => int + + deprecated: The fixed-value `@as(...) _` external argument syntax is deprecated. Use `%raw(...)` instead. + + + Warning number 3 + syntax_tests/data/printer/typexpr/fixedExternal.res:34:31-40 + + 32 │ external rawCommented: (/* a */ %raw("1") /* b */, %raw(/* c */ "2"), i + │ nt) => int = "f" + 33 │ + 34 │ @val external legacyNumbers: (@as(007) _, @as(1_000) _, int) => int = " + │ f" + 35 │ + 36 │ @val external templateSyntax: (@as("${x}") _, @as("a`b") _, @as("\\`") + │ _, int) => int = "f" + + deprecated: The fixed-value `@as(...) _` external argument syntax is deprecated. Use `%raw(...)` instead. + + + Warning number 3 + syntax_tests/data/printer/typexpr/fixedExternal.res:34:43-54 + + 32 │ external rawCommented: (/* a */ %raw("1") /* b */, %raw(/* c */ "2"), i + │ nt) => int = "f" + 33 │ + 34 │ @val external legacyNumbers: (@as(007) _, @as(1_000) _, int) => int = " + │ f" + 35 │ + 36 │ @val external templateSyntax: (@as("${x}") _, @as("a`b") _, @as("\\`") + │ _, int) => int = "f" + + deprecated: The fixed-value `@as(...) _` external argument syntax is deprecated. Use `%raw(...)` instead. + + + Warning number 3 + syntax_tests/data/printer/typexpr/fixedExternal.res:36:32-44 + + 34 │ @val external legacyNumbers: (@as(007) _, @as(1_000) _, int) => int = " + │ f" + 35 │ + 36 │ @val external templateSyntax: (@as("${x}") _, @as("a`b") _, @as("\\`") + │ _, int) => int = "f" + 37 │ + + deprecated: The fixed-value `@as(...) _` external argument syntax is deprecated. Use `%raw(...)` instead. + + + Warning number 3 + syntax_tests/data/printer/typexpr/fixedExternal.res:36:47-58 + + 34 │ @val external legacyNumbers: (@as(007) _, @as(1_000) _, int) => int = " + │ f" + 35 │ + 36 │ @val external templateSyntax: (@as("${x}") _, @as("a`b") _, @as("\\`") + │ _, int) => int = "f" + 37 │ + + deprecated: The fixed-value `@as(...) _` external argument syntax is deprecated. Use `%raw(...)` instead. + + + Warning number 3 + syntax_tests/data/printer/typexpr/fixedExternal.res:36:61-72 + + 34 │ @val external legacyNumbers: (@as(007) _, @as(1_000) _, int) => int = " + │ f" + 35 │ + 36 │ @val external templateSyntax: (@as("${x}") _, @as("a`b") _, @as("\\`") + │ _, int) => int = "f" + 37 │ + + deprecated: The fixed-value `@as(...) _` external argument syntax is deprecated. Use `%raw(...)` instead. + @val -external unsafeAddStyle: (@as(json`{}`) _, t, {..}) => t = "Object.assign" +external unsafeAddStyle: (%raw("{}"), t, {..}) => t = "Object.assign" @module("react") -external useEffect0: (unit => option unit>, @as(json`[]`) _) => unit = "useEffect" +external useEffect0: (unit => option unit>, %raw("[]")) => unit = "useEffect" -@send external createElement: (document, @as("img") _) => element = "createElement" +@send external createElement: (document, %raw(`"img"`)) => element = "createElement" -@val external templateString: (@as(`ab`) _, int) => int = "f" +@val external templateString: (%raw(`"ab"`), int) => int = "f" -@val external numbers: (@as(1) _, @as(-1) _, @as(0x10) _, int) => int = "f" +@val external numbers: (%raw("1"), %raw("-1"), %raw("0x10"), int) => int = "f" @obj external makeOptions: ( ~objectMode: @as(false) _, ~name: string, - ~mode: @as("fast") _, + ~mode: %raw(`"fast"`), unit, ) => options = "" -@val external single: @as(1) _ => int = "f" +@val external single: (%raw("1")) => int = "f" -@val external attributes: (@foo @as(1) _, @as(1) @foo _, @as({1}) _, int) => int = "f" +@val external attributes: (@foo %raw("1"), @foo %raw("1"), %raw("1"), int) => int = "f" @val -external commented: (/* a */ /* b */ @as(1) _ /* c */, /* d */ @as("s") _, int) => int = "f" +external commented: (/* a */ /* b */ %raw("1") /* c */, /* d */ %raw(`"s"`), int) => int = "f" type notExternal = (@as(1) _, int) => int @val external payloadAttributes: (@as(@foo (1)) _, @as(@foo (json`{}`)) _, int) => int = "f" + +@val +external raw: (%raw("{}"), %raw(`"img"`), ~mode: %raw("[1, 2]"), int) => int = "f" + +@val external singleRaw: (%raw("null")) => int = "f" + +@val +external rawCommented: (/* a */ %raw("1") /* b */, /* c */ %raw("2"), int) => int = "f" + +@val external legacyNumbers: (%raw("7"), %raw("1000"), int) => int = "f" + +@val external templateSyntax: (%raw(`"\${x}"`), %raw(`"a\`b"`), %raw(`"\\\\\`"`), int) => int = "f" diff --git a/tests/syntax_tests/data/printer/typexpr/fixedExternal.res b/tests/syntax_tests/data/printer/typexpr/fixedExternal.res index 2d6fcb16ffc..a03ef2bc272 100644 --- a/tests/syntax_tests/data/printer/typexpr/fixedExternal.res +++ b/tests/syntax_tests/data/printer/typexpr/fixedExternal.res @@ -22,3 +22,15 @@ external commented: (/* a */ @as(1) /* b */ _ /* c */, @as(/* d */ "s") _, int) type notExternal = (@as(1) _, int) => int @val external payloadAttributes: (@as(@foo 1) _, @as(@foo json`{}`) _, int) => int = "f" + +@val +external raw: (%raw("{}"), %raw(`"img"`), ~mode: %raw("[1, 2]"), int) => int = "f" + +@val external singleRaw: %raw("null") => int = "f" + +@val +external rawCommented: (/* a */ %raw("1") /* b */, %raw(/* c */ "2"), int) => int = "f" + +@val external legacyNumbers: (@as(007) _, @as(1_000) _, int) => int = "f" + +@val external templateSyntax: (@as("${x}") _, @as("a`b") _, @as("\\`") _, int) => int = "f" diff --git a/tests/tests/src/AsInUncurriedExternals.mjs b/tests/tests/src/AsInUncurriedExternals.mjs index 9af4134fcfe..c86b4449f82 100644 --- a/tests/tests/src/AsInUncurriedExternals.mjs +++ b/tests/tests/src/AsInUncurriedExternals.mjs @@ -19,15 +19,22 @@ function shouldNotFail(objectMode, name) { return 3; } -let x = somescope.somefn({foo:true}); +let x = somescope.somefn({"foo":true}); let y = somescope.stringfn("ab"); +let style = Object.assign({}, { + color: "red" +}, { + display: "flex" +}); + export { mo, options, shouldNotFail, x, y, + style, } /* x Not a pure module */ diff --git a/tests/tests/src/AsInUncurriedExternals.res b/tests/tests/src/AsInUncurriedExternals.res index 739f8668ec7..371ae00f68f 100644 --- a/tests/tests/src/AsInUncurriedExternals.res +++ b/tests/tests/src/AsInUncurriedExternals.res @@ -2,9 +2,9 @@ @obj external makeOptions: ( - ~objectMode: @as(json`false`) _, + ~objectMode: %raw("false"), ~name: string, - ~someOther: @as(json`true`) _, + ~someOther: %raw("true"), unit, ) => int = "" @@ -15,11 +15,18 @@ let options = mo(~name="foo", ()) let shouldNotFail: (~objectMode: _, ~name: string) => int = (~objectMode, ~name) => 3 @scope("somescope") -external constantArgOnly: @as(json`{foo:true}`) _ => string = "somefn" +external constantArgOnly: (%raw(`{"foo":true}`)) => string = "somefn" let x = constantArgOnly() @scope("somescope") -external semanticStringArg: @as(`\x61\u0062`) _ => string = "stringfn" +external semanticStringArg: (%raw(`"ab"`)) => string = "stringfn" let y = semanticStringArg() + +type t = {"color": string} + +@val +external unsafeAddStyle: (%raw("{}"), t, {..}) => t = "Object.assign" + +let style = unsafeAddStyle({"color": "red"}, {"display": "flex"}) diff --git a/tests/tests/src/UncurriedExternals.res b/tests/tests/src/UncurriedExternals.res index af1e40ac679..8258284c602 100644 --- a/tests/tests/src/UncurriedExternals.res +++ b/tests/tests/src/UncurriedExternals.res @@ -15,7 +15,7 @@ module StandardNotation = { @get_index external get: (array, int) => option<'a> = "" let tg = arr => arr->get(0) - @val external copy: (@as(json`{}`) _, string) => string = "Object.assign" + @val external copy: (%raw("{}"), string) => string = "Object.assign" let tc = copy("abc") external toException: exn => exn = "%identity" diff --git a/tests/tests/src/braced_attribute_payload.res b/tests/tests/src/braced_attribute_payload.res index bd5785cde41..20a19ccd259 100644 --- a/tests/tests/src/braced_attribute_payload.res +++ b/tests/tests/src/braced_attribute_payload.res @@ -9,7 +9,7 @@ external abs: int => int = "abs" external nested: unit => float = "random" @val -external jsonArg: (@as({json`[]`}) _, int) => int = "Math.abs" +external jsonArg: (%raw("[]"), int) => int = "Math.abs" type choice = | @as(1) First diff --git a/tests/tests/src/bs_auto_uncurry.res b/tests/tests/src/bs_auto_uncurry.res index 507e03087e6..86cdecb95ab 100644 --- a/tests/tests/src/bs_auto_uncurry.res +++ b/tests/tests/src/bs_auto_uncurry.res @@ -21,9 +21,9 @@ let xs = map([(1, 2), (1, 2), (2, 1)], ((x, y)) => y + x + 1) @val external ff: (int, @ignore int, (int, int) => int) => int = "ff" -@val external ff1: (int, @as(3) _, (int, int) => int) => int = "ff1" +@val external ff1: (int, %raw("3"), (int, int) => int) => int = "ff1" -@val external ff2: (int, @as("3") _, (int, int) => int) => int = "ff2" +@val external ff2: (int, %raw(`"3"`), (int, int) => int) => int = "ff2" @val external hi: (unit => unit) => int = "hi" diff --git a/tests/tests/src/bs_splice_partial.res b/tests/tests/src/bs_splice_partial.res index 21633ee1542..38acbd6a833 100644 --- a/tests/tests/src/bs_splice_partial.res +++ b/tests/tests/src/bs_splice_partial.res @@ -5,7 +5,7 @@ type t @send @variadic -external on_exit_slice3: (t, int, ~h: @as(3) _, @as("xxx") _, array) => unit = "xx" +external on_exit_slice3: (t, int, ~h: %raw("3"), %raw(`"xxx"`), array) => unit = "xx" let test = g => on_exit_slice3(g, __LINE__, [1, 2, 3]) @@ -46,6 +46,6 @@ type u = int => int let f = x => ignore(v(x)) -@val external fff0: (int, int, @as(json`[undefined,undefined]`) _) => int = "say" +@val external fff0: (int, int, %raw("[undefined,undefined]")) => int = "say" let testUndefined = () => fff0(1, 2) diff --git a/tests/tests/src/external_ppx.mjs b/tests/tests/src/external_ppx.mjs index 41d0d27f607..15474568163 100644 --- a/tests/tests/src/external_ppx.mjs +++ b/tests/tests/src/external_ppx.mjs @@ -10,7 +10,7 @@ let renamed = { let u = { hi: 2, lo: 3, - lo2: {hi:-3 }, + lo2: {hi: -3}, lo3: -1, lo4: -3 }; diff --git a/tests/tests/src/external_ppx.res b/tests/tests/src/external_ppx.res index 8c6d0e3a534..119de45062b 100644 --- a/tests/tests/src/external_ppx.res +++ b/tests/tests/src/external_ppx.res @@ -21,10 +21,10 @@ let renamed = renamed_make(~_type="123", ~normal=12.) @obj external ff: ( ~hi: int, - ~lo: @as(3) _, - ~lo2: @as(json`{hi:-3 }`) _, - ~lo3: @as(-1) _, - ~lo4: @as(json`-3`) _, + ~lo: %raw("3"), + ~lo2: %raw("{hi: -3}"), + ~lo3: %raw("-1"), + ~lo4: %raw("-3"), ) => _ = "" let u = ff(~hi=2) diff --git a/tests/tests/src/external_ppx2.res b/tests/tests/src/external_ppx2.res index fd515ec20e0..9ff056417c9 100644 --- a/tests/tests/src/external_ppx2.res +++ b/tests/tests/src/external_ppx2.res @@ -1,4 +1,4 @@ -external f: (@as("\h\e\l\lo") _, int) => unit = "f" +external f: (%raw(`"hello"`), int) => unit = "f" let x = "\h\e\l\lo" let y = f(42) diff --git a/tests/tests/src/ffi_js_test.res b/tests/tests/src/ffi_js_test.res index 66f4f7f589d..0409cea6e8e 100644 --- a/tests/tests/src/ffi_js_test.res +++ b/tests/tests/src/ffi_js_test.res @@ -47,12 +47,12 @@ type t @get_index external getGADTI2: (t, @ignore kind<'a>, @ignore kind<'b>, int) => ('a, 'b) = "" -@get_index external getGADTI3: (t, @ignore kind<'a>, @ignore kind<'b>, @as(3) _) => ('a, 'b) = "" +@get_index external getGADTI3: (t, @ignore kind<'a>, @ignore kind<'b>, %raw("3")) => ('a, 'b) = "" @set_index external setGADTI2: (t, @ignore kind<'a>, @ignore kind<'b>, int, ('a, 'b)) => unit = "" @set_index -external setGADTI3: (t, @ignore kind<'a>, @ignore kind<'b>, @as(3) _, ('a, 'b)) => unit = "" +external setGADTI3: (t, @ignore kind<'a>, @ignore kind<'b>, %raw("3"), ('a, 'b)) => unit = "" describe(__MODULE__, () => { test("higher_order function", () => eq(__LOC__, 6, higher_order(1)(2, 3))) diff --git a/tests/tests/src/gpr_1170.res b/tests/tests/src/gpr_1170.res index 42ac38e0bd7..3559e09bcae 100644 --- a/tests/tests/src/gpr_1170.res +++ b/tests/tests/src/gpr_1170.res @@ -1,7 +1,7 @@ type resp -@set external set_okay: (resp, @as(200) _) => unit = "statusCode" +@set external set_okay: (resp, %raw("200")) => unit = "statusCode" -@set external set_hi: (resp, @as("hi") _) => unit = "hi" +@set external set_hi: (resp, %raw(`"hi"`)) => unit = "hi" let f = resp => { set_okay(resp) diff --git a/tests/tests/src/gpr_1484.res b/tests/tests/src/gpr_1484.res index 3a5a6ca32c7..0bb0427e6fc 100644 --- a/tests/tests/src/gpr_1484.res +++ b/tests/tests/src/gpr_1484.res @@ -1,5 +1,5 @@ type t -@set external clearNodeValue: (t, @as(json`null`) _) => unit = "nodeValue" +@set external clearNodeValue: (t, %raw("null")) => unit = "nodeValue" /* TODO: more test cases */ /* external clearNodeValue2 : */ diff --git a/tests/tests/src/mario_game.res b/tests/tests/src/mario_game.res index 20345edbbe7..e7d81f58f49 100644 --- a/tests/tests/src/mario_game.res +++ b/tests/tests/src/mario_game.res @@ -160,7 +160,7 @@ module Dom_html = { @val external window: Dom.window = "window" /* external createImg: (_ [@as "img"]) -> document -> imageElement = "createElement" [@@send] */ - @send external createImg: (Dom.document, @as("img") _) => imageElement = "createElement" + @send external createImg: (Dom.document, %raw(`"img"`)) => imageElement = "createElement" @val external requestAnimationFrame: (float => unit) => unit = "requestAnimationFrame" @return(null_to_opt) @send external getElementById: (Dom.document, string) => option = "getElementById" diff --git a/tests/tests/src/prepend_data_ffi.mjs b/tests/tests/src/prepend_data_ffi.mjs index 9ae19366a9c..8cf59713d86 100644 --- a/tests/tests/src/prepend_data_ffi.mjs +++ b/tests/tests/src/prepend_data_ffi.mjs @@ -22,19 +22,19 @@ process.on(i => i.toString(), 1); xx(3, 3, "xxx", "a", "b"); function f(x) { - x.xx(72, [ + x.xx(73, [ 1, 2, 3 ]); - x.xx(73, 3, "xxx", [ + x.xx(74, 3, "xxx", [ 1, 2, 3 ]); - x.xx(74, 3, "xxx", 1, 2, 3); - x.xx(75, 3, "xxx", 0, "b", 1, 2, 3, 4, 5); - x.xx(76, 3, true, false, "你好", ["你好",1,2,3] , [{ "arr" : ["你好",1,2,3], "encoding" : "utf8"}] , [{ "arr" : ["你好",1,2,3], "encoding" : "utf8"}] , "xxx", 0, "yyy", "b", 1, 2, 3, 4, 5); + x.xx(75, 3, "xxx", 1, 2, 3); + x.xx(76, 3, "xxx", 0, "b", 1, 2, 3, 4, 5); + x.xx(77, 3, true, false, "你好", ["你好",1,2,3], [{"arr":["你好",1,2,3],"encoding":"utf8"}], [{"arr":["你好",1,2,3],"encoding":"utf8"}], "xxx", 0, "yyy", "b", 1, 2, 3, 4, 5); } process.on("exit", exit_code => { diff --git a/tests/tests/src/prepend_data_ffi.res b/tests/tests/src/prepend_data_ffi.res index 33802f4e1b6..b05e4a49fa2 100644 --- a/tests/tests/src/prepend_data_ffi.res +++ b/tests/tests/src/prepend_data_ffi.res @@ -1,30 +1,30 @@ type config1_expect = {"v": int} -@obj external config1: (~stdio: @as("inherit") _, ~v: int, unit) => _ = "" +@obj external config1: (~stdio: %raw(`"inherit"`), ~v: int, unit) => _ = "" let v1: config1_expect = config1(~v=3, ()) type config2_expect = {"v": int} -@obj external config2: (~stdio: @as(1) _, ~v: int, unit) => _ = "" +@obj external config2: (~stdio: %raw("1"), ~v: int, unit) => _ = "" let v2: config2_expect = config2(~v=2, ()) -@val external on_exit: (@as("exit") _, int => string) => unit = "process.on" +@val external on_exit: (%raw(`"exit"`), int => string) => unit = "process.on" let () = on_exit(exit_code => Int.toString(exit_code)) -@val external on_exit_int: (@as(1) _, int => unit) => unit = "process.on" +@val external on_exit_int: (%raw("1"), int => unit) => unit = "process.on" let () = on_exit_int(_ => ()) -@val external on_exit3: (int => string, @as("exit") _) => unit = "process.on" +@val external on_exit3: (int => string, %raw(`"exit"`)) => unit = "process.on" let () = on_exit3(i => Int.toString(i)) -@val external on_exit4: (int => string, @as(1) _) => unit = "process.on" +@val external on_exit4: (int => string, %raw("1")) => unit = "process.on" let () = on_exit4(i => Int.toString(i)) -@val @variadic external on_exit_slice: (int, @as(3) _, @as("xxx") _, array) => unit = "xx" +@val @variadic external on_exit_slice: (int, %raw("3"), %raw(`"xxx"`), array) => unit = "xx" let () = on_exit_slice(3, ["a", "b"]) @@ -32,16 +32,17 @@ type t @send external on_exit_slice1: (t, int, array) => unit = "xx" -@send external on_exit_slice2: (t, int, @as(3) _, @as("xxx") _, array) => unit = "xx" +@send external on_exit_slice2: (t, int, %raw("3"), %raw(`"xxx"`), array) => unit = "xx" -@send @variadic external on_exit_slice3: (t, int, @as(3) _, @as("xxx") _, array) => unit = "xx" +@send @variadic +external on_exit_slice3: (t, int, %raw("3"), %raw(`"xxx"`), array) => unit = "xx" @send @variadic external on_exit_slice4: ( t, int, - @as(3) _, - @as("xxx") _, + %raw("3"), + %raw(`"xxx"`), @int [#a | #b | #c], [#a | #b | #c], array, @@ -51,16 +52,16 @@ external on_exit_slice4: ( external on_exit_slice5: ( t, int, - @as(3) _, - @as(json`true`) _, - @as(json`false`) _, - @as(json`"你好"`) _, - @as(json` ["你好",1,2,3] `) _, - @as(json` [{ "arr" : ["你好",1,2,3], "encoding" : "utf8"}] `) _, - @as(json` [{ "arr" : ["你好",1,2,3], "encoding" : "utf8"}] `) _, - @as("xxx") _, + %raw("3"), + %raw("true"), + %raw("false"), + %raw(`"你好"`), + %raw(`["你好",1,2,3]`), + %raw(`[{"arr":["你好",1,2,3],"encoding":"utf8"}]`), + %raw(`[{"arr":["你好",1,2,3],"encoding":"utf8"}]`), + %raw(`"xxx"`), @int [#a | #b | #c], - @as("yyy") _, + %raw(`"yyy"`), [#a | #b | #c], array, ) => unit = "xx" @@ -76,15 +77,15 @@ let f = (x: t) => { x->on_exit_slice5(__LINE__, #a, #b, [1, 2, 3, 4, 5]) } -@val external process_on_exit: (@as("exit") _, int => unit) => unit = "process.on" +@val external process_on_exit: (%raw(`"exit"`), int => unit) => unit = "process.on" let () = process_on_exit(exit_code => Console.log2("error code: %d", exit_code)) type process -@send external on_exit: (process, @as("exit") _, int => unit) => unit = "on" +@send external on_exit: (process, %raw(`"exit"`), int => unit) => unit = "on" let register = (p: process) => p->on_exit(i => Console.log(i)) -@obj external io_config: (~stdio: @as("inherit") _, ~cwd: string, unit) => _ = "" +@obj external io_config: (~stdio: %raw(`"inherit"`), ~cwd: string, unit) => _ = "" let config = io_config(~cwd=".", ()) diff --git a/tests/tests/src/record_name_test.res b/tests/tests/src/record_name_test.res index 4d5b6bd9251..9f2a6f03e99 100644 --- a/tests/tests/src/record_name_test.res +++ b/tests/tests/src/record_name_test.res @@ -64,8 +64,8 @@ type t6 = { } /* allow this case */ -@obj external ff: (~x: int, ~h: @as(3) _) => _ = "" -@obj external ff2: (~x: int, ~h: @as(3) _) => {"x": int} = "" +@obj external ff: (~x: int, ~h: %raw("3")) => _ = "" +@obj external ff2: (~x: int, ~h: %raw("3")) => {"x": int} = "" let u = () => { ignore(ff(~x=3)) ff2(~x=22)