Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#### :boom: Breaking Change

- Distinguish multiple constructor arguments from a tuple passed as a single argument. Constructors with one tuple payload must now use nested parentheses, for example `Some((x, y))`; `Some(x, y)` now reports an arity mismatch. This makes constructor arity explicit in the parsetree and removes the separate parser modes for printing and type checking. https://github.com/rescript-lang/rescript/pull/8610
- Reject malformed UTF-8 in documentation comments and invalid string or template literal escapes that were previously accepted, including empty or out-of-range braced Unicode escapes (`\u{}`, `\u{110000}`) and legacy decimal or octal escapes in templates (`\1`, `\01`, `\8`). These inputs now produce syntax diagnostics instead of compiling to invalid or inconsistent JavaScript. https://github.com/rescript-lang/rescript/pull/8606
- Reject tagged template literals in patterns. Patterns cannot invoke their tag; previously their raw payload was compiled as a plain string comparison. https://github.com/rescript-lang/rescript/pull/8606
- Remove runtime APIs that were deprecated for removal in ReScript 13, including the `Char` module, unsafe `Obj` operations, legacy `Pervasives` helpers, and `Array.unsafe_get`. https://github.com/rescript-lang/rescript/pull/8564
Expand Down
9 changes: 5 additions & 4 deletions analysis/reanalyze/src/annotation.ml
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,11 @@ let rec get_attribute_payload check_text (attributes : Typedtree.attributes) =
_;
} ->
Some (BoolPayload (s = "true"))
| {pexp_desc = Pexp_construct ({txt = Longident.Lident "[]"}, None)} ->
None
| {pexp_desc = Pexp_construct ({txt = Longident.Lident "::"}, Some e)} ->
from_expr e
| {pexp_desc = Pexp_construct ({txt = Longident.Lident "[]"}, [])} -> None
| {
pexp_desc = Pexp_construct ({txt = Longident.Lident "::"}, [head; tail]);
} ->
from_expr {expr with pexp_desc = Pexp_tuple [head; tail]}
| {pexp_desc = Pexp_construct ({txt}, _); _} ->
Some (ConstructPayload (txt |> Longident.flatten |> String.concat "."))
| {pexp_desc = Pexp_tuple exprs | Pexp_array exprs} ->
Expand Down
4 changes: 2 additions & 2 deletions analysis/src/codemod.ml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ let transform_opt ~source ~pos ~debug ~typ ~hint =
| AddMissingCases -> (
let source = "let " ^ hint ^ " = ()" in
let {Res_driver.parsetree = hint_structure} =
Res_driver.parse_implementation_from_source ~for_printer:false
~display_filename:"<none>" ~source
Res_driver.parse_implementation_from_source ~display_filename:"<none>"
~source
in
match hint_structure with
| [{pstr_desc = Pstr_value (_, [{pvb_pat = pattern}])}] -> (
Expand Down
6 changes: 2 additions & 4 deletions analysis/src/commands.ml
Original file line number Diff line number Diff line change
Expand Up @@ -304,8 +304,7 @@ let format ~source ~kind_file =
match kind_file with
| Files.Res -> (
let {Res_driver.parsetree = structure; comments; diagnostics} =
Res_driver.parsing_engine.parse_implementation_from_source
~for_printer:true ~source
Res_driver.parsing_engine.parse_implementation_from_source ~source
in
match List.length diagnostics > 0 with
| true -> Error "Document has syntax errors"
Expand All @@ -314,8 +313,7 @@ let format ~source ~kind_file =
)
| Resi -> (
let {Res_driver.parsetree = signature; comments; diagnostics} =
Res_driver.parsing_engine.parse_interface_from_source ~for_printer:true
~source
Res_driver.parsing_engine.parse_interface_from_source ~source
in
match List.length diagnostics > 0 with
| true -> Error "Document has syntax errors"
Expand Down
86 changes: 20 additions & 66 deletions analysis/src/completion_expressions.ml
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ let rec traverse_expr (exp : Parsetree.expression) ~expr_path ~pos
(txt, [Completable.NRecordBody {seen_fields = []}] @ expr_path)
| Pexp_ident {txt = Lident txt} -> some_if_has_cursor (txt, expr_path)
| Pexp_construct ({txt = Lident "()"}, _) -> some_if_has_cursor ("", expr_path)
| Pexp_construct ({txt = Lident txt}, None) ->
| Pexp_construct ({txt = Lident txt}, []) ->
some_if_has_cursor (txt, expr_path)
| Pexp_variant (label, None) -> some_if_has_cursor ("#" ^ label, expr_path)
| Pexp_variant (label, []) -> some_if_has_cursor ("#" ^ label, expr_path)
| Pexp_array array_patterns -> (
let next_expr_path = [Completable.NArray] @ expr_path in
(* No fields but still has cursor = empty completion *)
Expand Down Expand Up @@ -121,8 +121,7 @@ let rec traverse_expr (exp : Parsetree.expression) ~expr_path ~pos
("", [Completable.NRecordBody {seen_fields}] @ expr_path)
| _ -> None))
| Pexp_construct
( {txt},
Some {pexp_loc; pexp_desc = Pexp_construct ({txt = Lident "()"}, _)} )
({txt}, [{pexp_loc; pexp_desc = Pexp_construct ({txt = Lident "()"}, _)}])
when loc_has_cursor pexp_loc ->
(* Empty payload with cursor, like: Test(<com>) *)
Some
Expand All @@ -132,21 +131,24 @@ let rec traverse_expr (exp : Parsetree.expression) ~expr_path ~pos
{constructor_name = Utils.get_unqualified_name txt; item_num = 0};
]
@ expr_path )
| Pexp_construct ({txt}, Some e)
when pos >= (e.pexp_loc |> Loc.end_)
| Pexp_construct ({txt}, args)
when args <> []
&& pos >= ((Ext_list.last args).pexp_loc |> Loc.end_)
&& first_char_before_cursor_no_white = Some ','
&& is_expr_tuple e = false ->
&& is_expr_tuple (Ext_list.last args) = false ->
(* Empty payload with trailing ',', like: Test(true, <com>) *)
Some
( "",
[
Completable.NVariantPayload
{constructor_name = Utils.get_unqualified_name txt; item_num = 1};
{
constructor_name = Utils.get_unqualified_name txt;
item_num = List.length args;
};
]
@ expr_path )
| Pexp_construct ({txt}, Some {pexp_loc; pexp_desc = Pexp_tuple tuple_items})
when loc_has_cursor pexp_loc ->
tuple_items
| Pexp_construct ({txt}, args) when loc_has_cursor exp.pexp_loc ->
args
|> traverse_expr_tuple_items ~first_char_before_cursor_no_white ~pos
~next_expr_path:(fun item_num ->
[
Expand All @@ -163,38 +165,16 @@ let rec traverse_expr (exp : Parsetree.expression) ~expr_path ~pos
};
]
@ expr_path)
| Pexp_construct ({txt}, Some p) when loc_has_cursor exp.pexp_loc ->
p
|> traverse_expr ~first_char_before_cursor_no_white ~pos
~expr_path:
([
Completable.NVariantPayload
{
constructor_name = Utils.get_unqualified_name txt;
item_num = 0;
};
]
@ expr_path)
| Pexp_variant
(txt, Some {pexp_loc; pexp_desc = Pexp_construct ({txt = Lident "()"}, _)})
(txt, [{pexp_loc; pexp_desc = Pexp_construct ({txt = Lident "()"}, _)}])
when loc_has_cursor pexp_loc ->
(* Empty payload with cursor, like: #test(<com>) *)
Some
( "",
[Completable.NPolyvariantPayload {constructor_name = txt; item_num = 0}]
@ expr_path )
| Pexp_variant (txt, Some e)
when pos >= (e.pexp_loc |> Loc.end_)
&& first_char_before_cursor_no_white = Some ','
&& is_expr_tuple e = false ->
(* Empty payload with trailing ',', like: #test(true, <com>) *)
Some
( "",
[Completable.NPolyvariantPayload {constructor_name = txt; item_num = 1}]
@ expr_path )
| Pexp_variant (txt, Some {pexp_loc; pexp_desc = Pexp_tuple tuple_items})
when loc_has_cursor pexp_loc ->
tuple_items
| Pexp_variant (txt, args) when loc_has_cursor exp.pexp_loc ->
args
|> traverse_expr_tuple_items ~first_char_before_cursor_no_white ~pos
~next_expr_path:(fun item_num ->
[Completable.NPolyvariantPayload {constructor_name = txt; item_num}]
Expand All @@ -205,15 +185,6 @@ let rec traverse_expr (exp : Parsetree.expression) ~expr_path ~pos
{constructor_name = txt; item_num = item_num + 1};
]
@ expr_path)
| Pexp_variant (txt, Some p) when loc_has_cursor exp.pexp_loc ->
p
|> traverse_expr ~first_char_before_cursor_no_white ~pos
~expr_path:
([
Completable.NPolyvariantPayload
{constructor_name = txt; item_num = 0};
]
@ expr_path)
| _ -> None

and traverse_expr_tuple_items tuple_items ~next_expr_path
Expand Down Expand Up @@ -280,35 +251,18 @@ let pretty_print_fn_template_arg_name ?current_index ~env ~state ~full
| _ -> default_var_name)

let complete_constructor_payload ~pos_before_cursor
~first_char_before_cursor_no_white
~first_char_before_cursor_no_white ~item_num
(constructor_lid : Longident.t Location.loc) expr =
match
traverse_expr expr ~expr_path:[] ~pos:pos_before_cursor
~first_char_before_cursor_no_white
with
| None -> None
| Some (prefix, nested) ->
(* The nested path must start with the constructor name found, plus
the target argument number for the constructor. We translate to
that here, because we need to account for multi arg constructors
being represented as tuples. *)
let nested =
match List.rev nested with
| Completable.NTupleItem {item_num} :: rest ->
[
Completable.NVariantPayload
{constructor_name = Longident.last constructor_lid.txt; item_num};
]
@ rest
| nested ->
[
Completable.NVariantPayload
{
constructor_name = Longident.last constructor_lid.txt;
item_num = 0;
};
]
@ nested
Completable.NVariantPayload
{constructor_name = Longident.last constructor_lid.txt; item_num}
:: List.rev nested
in
let variant_ctx_path =
Completable.CTypeAtPos
Expand Down
77 changes: 32 additions & 45 deletions analysis/src/completion_front_end.ml
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ let rec expr_to_context_path_inner ~(in_jsx_context : bool)
| None -> None)
| Pexp_constant (Pconst_integer _) -> Some CPInt
| Pexp_constant (Pconst_float _) -> Some CPFloat
| Pexp_construct ({txt = Lident ("true" | "false")}, None) -> Some CPBool
| Pexp_construct ({txt = Lident ("true" | "false")}, []) -> Some CPBool
| Pexp_array exprs ->
Some
(CPArray
Expand Down Expand Up @@ -492,9 +492,9 @@ let completion_with_parser1 ~debug ~offset ~pos_cursor ~kind_file
scope_pattern p
~pattern_path:(NTupleItem {item_num = index} :: pattern_path)
?context_path)
| Ppat_construct (_, None) -> ()
| Ppat_construct ({txt}, Some {ppat_desc = Ppat_tuple pl}) ->
pl
| Ppat_construct (_, []) -> ()
| Ppat_construct ({txt}, patterns) ->
patterns
|> List.iteri (fun index p ->
scope_pattern p
~pattern_path:
Expand All @@ -505,28 +505,15 @@ let completion_with_parser1 ~debug ~offset ~pos_cursor ~kind_file
}
:: pattern_path)
?context_path)
| Ppat_construct ({txt}, Some p) ->
scope_pattern
~pattern_path:
(NVariantPayload
{item_num = 0; constructor_name = Utils.get_unqualified_name txt}
:: pattern_path)
?context_path p
| Ppat_variant (_, None) -> ()
| Ppat_variant (txt, Some {ppat_desc = Ppat_tuple pl}) ->
pl
| Ppat_variant (_, []) -> ()
| Ppat_variant (txt, patterns) ->
patterns
|> List.iteri (fun index p ->
scope_pattern p
~pattern_path:
(NPolyvariantPayload {item_num = index; constructor_name = txt}
:: pattern_path)
?context_path)
| Ppat_variant (txt, Some p) ->
scope_pattern
~pattern_path:
(NPolyvariantPayload {item_num = 0; constructor_name = txt}
:: pattern_path)
?context_path p
| Ppat_record (fields, _, rest) -> (
Ext_list.iter fields (fun {lid = fname; x = p} ->
match fname with
Expand Down Expand Up @@ -1043,7 +1030,7 @@ let completion_with_parser1 ~debug ~offset ~pos_cursor ~kind_file
Pstr_eval
( {
pexp_loc;
pexp_desc = Pexp_construct ({txt = path; loc}, None);
pexp_desc = Pexp_construct ({txt = path; loc}, []);
},
_ );
};
Expand Down Expand Up @@ -1283,36 +1270,41 @@ let completion_with_parser1 ~debug ~offset ~pos_cursor ~kind_file
then ValueOrField
else Value);
}))
| Pexp_construct (lid, e_opt) -> (
| Pexp_construct (lid, args) ->
let lid_path = flatten_lid_check_dot lid in
if debug then
Printf.printf "Pexp_construct %s:%s %s\n"
(lid_path |> String.concat "\n")
(Loc.to_string lid.loc)
(match e_opt with
| None -> "None"
| Some e -> Loc.to_string e.pexp_loc);
(match args with
| [] -> "None"
| args ->
args
|> List.map (fun (e : Parsetree.expression) ->
Loc.to_string e.pexp_loc)
|> String.concat ", ");
if
e_opt = None && (not lid.loc.loc_ghost)
args = [] && (not lid.loc.loc_ghost)
&& lid.loc |> Loc.has_pos ~pos:pos_before_cursor
then
set_result
(Cpath
(CPId
{loc = lid.loc; path = lid_path; completion_context = Value}))
else
match e_opt with
| Some e when loc_has_cursor e.pexp_loc -> (
match
Completion_expressions.complete_constructor_payload
~pos_before_cursor ~first_char_before_cursor_no_white lid e
with
| Some result ->
(* Check if anything else more important completes before setting this completion. *)
Ast_iterator.default_iterator.expr iterator e;
set_result result
| None -> ())
| _ -> ())
args
|> List.iteri (fun item_num (e : Parsetree.expression) ->
if loc_has_cursor e.pexp_loc then
match
Completion_expressions.complete_constructor_payload
~pos_before_cursor ~first_char_before_cursor_no_white
~item_num lid e
with
| Some result ->
(* Check if anything else more important completes before setting this completion. *)
Ast_iterator.default_iterator.expr iterator e;
set_result result
| None -> ())
| Pexp_field (e, field_name) -> (
if debug then
Printf.printf "Pexp_field %s %s:%s\n" (Loc.to_string e.pexp_loc)
Expand Down Expand Up @@ -1864,10 +1856,7 @@ let completion_with_parser1 ~debug ~offset ~pos_cursor ~kind_file
in

if kind_file = Files.Res then (
let parser =
Res_driver.parsing_engine.parse_implementation_from_source
~for_printer:false
in
let parser = Res_driver.parsing_engine.parse_implementation_from_source in
let {Res_driver.parsetree = str} = parser ~source:text in
iterator.structure iterator str |> ignore;
if blank_after_cursor = Some ' ' || blank_after_cursor = Some '\n' then (
Expand All @@ -1878,9 +1867,7 @@ let completion_with_parser1 ~debug ~offset ~pos_cursor ~kind_file
if !found = false then if debug then Printf.printf "XXX Not found!\n";
!result)
else if kind_file = Resi then (
let parser =
Res_driver.parsing_engine.parse_interface_from_source ~for_printer:false
in
let parser = Res_driver.parsing_engine.parse_interface_from_source in
let {Res_driver.parsetree = signature} = parser ~source:text in
iterator.signature iterator signature |> ignore;
if blank_after_cursor = Some ' ' || blank_after_cursor = Some '\n' then (
Expand Down
Loading
Loading