Skip to content
Open
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
d429a17
Refactor constructor arity in parsetree
cknitt Sep 3, 2026
060e341
Remove obsolete parser printer flag
cknitt Sep 3, 2026
2e7dc0a
Share constructor pattern argument parsing
cknitt Sep 3, 2026
c901404
Share constructor argument printing
cknitt Sep 3, 2026
e7699f6
Centralize AST0 constructor argument bridging
cknitt Sep 3, 2026
41122f2
Localize legacy explicit arity handling
cknitt Sep 3, 2026
445faa1
Use plural names for constructor source arguments
cknitt Sep 3, 2026
6cd5e98
Add constructor arity changelog entry
cknitt Sep 3, 2026
0b18847
Preserve fresh AST0 constructor arity
cknitt Sep 3, 2026
e1f311b
Partition polymorphic variant argument comments
cknitt Sep 3, 2026
8bcc1e9
Fix AST0 constructor payload locations and printing
cknitt Sep 3, 2026
4a211d2
Bump compiled artifact versions for constructor AST changes
cknitt Sep 3, 2026
f7dc510
Deduplicate AST0 bridge marker removal
cknitt Sep 3, 2026
752edfd
Preserve polymorphic variant payload spans across AST0
cknitt Sep 4, 2026
3557f92
Retain constructor signature help between arguments
cknitt Sep 4, 2026
81a6608
Preserve constructor compatibility without parser modes
cknitt Sep 4, 2026
b557f77
Remove redundant single-tuple AST0 bridge marker
cknitt Sep 4, 2026
217380d
Revert unnecessary constructor syntax migrations
cknitt Sep 4, 2026
3b696a5
Simplify constructor bridge metadata handling and assertions
cknitt Sep 4, 2026
c2deae1
Clarify constructor argument naming and simplify traversal
cknitt Sep 4, 2026
4bb01e5
Rename constructor argument compatibility fixture
cknitt Sep 4, 2026
c19a8f2
Preserve constructor argument-list locations explicitly
cknitt Sep 4, 2026
fdc4934
Reuse argument-list locations in parsing and type checking
cknitt Sep 4, 2026
00ba77c
Use argument-list boundaries for constructor signature help
cknitt Sep 4, 2026
4988e0d
Accept located constructor arguments in AST helpers
cknitt Sep 4, 2026
c39f9c6
Separate constructor argument tests from AST0 bridge coverage
cknitt Sep 4, 2026
c803a1d
Document the constructor argument AST0 bridge contract
cknitt Sep 4, 2026
a6d5edc
Use explicit locations at constructor helper call sites
cknitt Sep 4, 2026
b1d9f71
Normalize constructor tuple paths in completion
cknitt Sep 4, 2026
de937e5
Select constructor tuple parameters in signature help
cknitt Sep 4, 2026
1324c16
Preserve constructor source arity in completion paths
cknitt Sep 4, 2026
759b36c
Use shared argument traversal for constructor completion gaps
cknitt Sep 4, 2026
cc3063c
Reuse resolved inline-record constructor fields in completion
cknitt Sep 4, 2026
6b84b9d
Preserve list constructor attributes on the AST0 wire
cknitt Sep 5, 2026
3ed6059
Simplify AST0 argument tuple callbacks
cknitt Sep 5, 2026
6d16043
Preserve attributed list payload tuples across AST0 conversions
cknitt Sep 5, 2026
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@

#### :bug: Bug fix

- Fix record-field completion inside tuple arguments of constructors with multiple arguments, in expressions and patterns. https://github.com/rescript-lang/rescript/pull/8610

- Limit constructor signature help to the argument parentheses, excluding whitespace and comments between the constructor name and its arguments. https://github.com/rescript-lang/rescript/pull/8610
- Fix excessive parentheses and indentation in function assignments to refs, align record and array assignment formatting across refs and fields, and preserve function return-type parentheses and consistent JSX fragment layout in callbacks. https://github.com/rescript-lang/rescript/pull/8611
- Fix a recursive module with an empty signature discarding its right-hand side. Lambda-to-Lam conversion rewrote `Pupdate_mod` to unit when the module's shape had no fields, dropping the primitive's arguments - one of which is the right-hand side - so `module rec M: {} = { let () = Console.log("effect") }` emitted nothing for `M`. The elision now happens where the bindings are produced, with the right-hand side still in hand. https://github.com/rescript-lang/rescript/pull/8608
- Fix `Int.Ref.increment` and `Int.Ref.decrement` evaluating their argument twice: `Int.Ref.increment(mkRef())` emitted `mkRef().contents = mkRef().contents + 1 | 0`. The `%incr` and `%decr` builtins lowered to an assignment that repeated the argument expression; they now bind the reference before the read-modify-write. Inlining decisions around an increment are taken on the code it stands for rather than on a single primitive node. https://github.com/rescript-lang/rescript/pull/8608
Expand Down Expand Up @@ -68,6 +71,7 @@

#### :house: Internal

- Remove separate parser modes for printing and type checking by preserving syntactic constructor arguments and their source locations in the parsetree and resolving their semantic grouping during type checking. Existing constructor spellings and legacy PPX output remain supported. https://github.com/rescript-lang/rescript/pull/8610
- Merge the duplicate Lam intermediate representation into Lambda, removing the conversion layer and obsolete supporting infrastructure. Lambda is now a single private, normalized representation, with generated JavaScript remaining semantically unchanged. https://github.com/rescript-lang/rescript/pull/8608
- Add genType and source map controls and output to the developer playground. https://github.com/rescript-lang/rescript/pull/8448
- Rework the object-type representation end to end: object rows are plain field chains carrying a per-field mutability state (no phantom setter members), object literals are typed directly and property access and assignment are first-class AST and Lambda nodes shared between the Lambda and JS pipelines, and dead class-system remnants (the field-presence lattice, the class-abbreviation memo on object types, method-send typing) are removed. https://github.com/rescript-lang/rescript/pull/8597
Expand Down
10 changes: 7 additions & 3 deletions analysis/reanalyze/src/annotation.ml
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,14 @@ let rec get_attribute_payload check_text (attributes : Typedtree.attributes) =
_;
} ->
Some (BoolPayload (s = "true"))
| {pexp_desc = Pexp_construct ({txt = Longident.Lident "[]"}, None)} ->
| {pexp_desc = Pexp_construct ({txt = Longident.Lident "[]"}, {txt = []})}
->
None
| {pexp_desc = Pexp_construct ({txt = Longident.Lident "::"}, Some e)} ->
from_expr e
| {
pexp_desc =
Pexp_construct ({txt = Longident.Lident "::"}, {txt = [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
113 changes: 33 additions & 80 deletions analysis/src/completion_expressions.ml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@ let is_expr_hole exp =
| Pexp_extension ({txt = "rescript.exprhole"}, _) -> true
| _ -> false

let is_expr_tuple expr =
match expr.Parsetree.pexp_desc with
| Pexp_tuple _ -> true
| _ -> false

let rec traverse_expr (exp : Parsetree.expression) ~expr_path ~pos
~first_char_before_cursor_no_white =
let loc_has_cursor loc = loc |> Cursor_position.loc_has_cursor ~pos in
Expand All @@ -24,9 +19,10 @@ 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}, {txt = []}) ->
some_if_has_cursor (txt, expr_path)
| Pexp_variant (label, None) -> some_if_has_cursor ("#" ^ label, expr_path)
| Pexp_variant (label, {txt = []}) ->
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 @@ -122,36 +118,33 @@ let rec traverse_expr (exp : Parsetree.expression) ~expr_path ~pos
| _ -> 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
( "",
[
Completable.NVariantPayload
{constructor_name = Utils.get_unqualified_name txt; item_num = 0};
]
@ expr_path )
| Pexp_construct ({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.NVariantPayload
{constructor_name = Utils.get_unqualified_name txt; item_num = 1};
{
constructor_name = Utils.get_unqualified_name txt;
item_num = 0;
source_arity = 1;
};
]
@ 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}, {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.NVariantPayload
{constructor_name = Utils.get_unqualified_name txt; item_num};
{
constructor_name = Utils.get_unqualified_name txt;
item_num;
source_arity = List.length args;
};
]
@ expr_path)
~result_from_found_item_num:(fun item_num ->
Expand All @@ -160,41 +153,23 @@ let rec traverse_expr (exp : Parsetree.expression) ~expr_path ~pos
{
constructor_name = Utils.get_unqualified_name txt;
item_num = item_num + 1;
source_arity = List.length args;
};
]
@ 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,
{
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, {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 +180,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 +246,22 @@ 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 ~source_arity
(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;
source_arity;
}
:: List.rev nested
Comment thread
cknitt marked this conversation as resolved.
in
let variant_ctx_path =
Completable.CTypeAtPos
Expand Down
Loading
Loading