Skip to content

Commit cd32c86

Browse files
cknittclaude
andcommitted
Use the applied signature for modules created by functor application
Why: Editor analysis modelled `module M = F(X)` by F's own body, so hover and completion on M's items showed the types of the functor's body, with the parameter's types such as `O.t`, instead of the argument's. M is now modelled by the signature the compiler computed for the application. That signature keeps the identifiers of F's body, whose stamps already name the body's items, so it is renamed first. Diagnostic modified (completion on an applied functor): Example: module type Ord = { type t let compare: (t, t) => int } module MakeSet = (O: Ord) => { type t = array<O.t> let add = (s: t, x: O.t) => s->Array.concat([x]) } module IntSet = MakeSet({ type t = int let compare = (a: int, b) => a - b }) // IntSet. Before (text): tests/analysis_tests/tests/src/AppliedFunctor.res "detail": "(t, O.t) => array<O.t>" After (text): tests/analysis_tests/tests/src/AppliedFunctor.res "detail": "(t, int) => array<int>" Diagnostic modified (hover on a type of an applied functor): Before (text): type IntSet.t = array<O.t> After (text): type IntSet.t = array<int> Review: Why raise the stamp counter before renaming? The renamed identifiers get stamps from the analysis process's own counter, which starts low and would hand out stamps that the typed tree already uses; above 2^40 they cannot meet one. Fixes: #9072 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Kbf3gLMawVgtmiKy3TMBc3 Signed-off-by: Christoph Knittel <ck@cca.io>
1 parent beb423d commit cd32c86

4 files changed

Lines changed: 78 additions & 10 deletions

File tree

‎analysis/src/process_cmt.ml‎

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,10 @@ and for_type_module ~name ~env module_type =
185185
| Mty_functor (_argIdent, _argType, result_type) ->
186186
for_type_module ~name ~env result_type
187187

188+
(* Stamps given to renamed identifiers start here, far above the stamps
189+
in a typed tree, so that they never meet one of them. *)
190+
let renamed_stamp_floor = 1 lsl 40
191+
188192
let get_module_type_path mod_desc =
189193
match mod_desc with
190194
| Typedtree.Tmty_ident (path, _) | Tmty_alias (path, _) -> Some path
@@ -546,13 +550,12 @@ let rec for_structure_item ~(env : Shared_types.Env.t) ~(exported : Exported.t)
546550
bindings
547551
|> List.iter (fun {Typedtree.vb_expr} -> scan_let_modules ~env vb_expr);
548552
!items
549-
| Tstr_module
550-
{mb_id; mb_attributes; mb_loc; mb_name = name; mb_expr = {mod_desc}}
553+
| Tstr_module {mb_id; mb_attributes; mb_loc; mb_name = name; mb_expr}
551554
when not
552555
(String.length name.txt >= 6
553556
&& (String.sub name.txt 0 6 = "local_") [@doesNotRaise])
554557
(* %%private generates a dummy module called local_... *) ->
555-
let item = for_module ~env mod_desc name.txt in
558+
let item = for_module ~env mb_expr name.txt in
556559
let declared =
557560
add_declared ~item ~name ~extent:mb_loc ~stamp:(Ident.binding_time mb_id)
558561
~env mb_attributes
@@ -647,8 +650,8 @@ let rec for_structure_item ~(env : Shared_types.Env.t) ~(exported : Exported.t)
647650
decl |> for_type_declaration ~env ~exported ~rec_status)
648651
| _ -> []
649652

650-
and for_module ~env mod_desc module_name =
651-
match mod_desc with
653+
and for_module ~env (mod_expr : Typedtree.module_expr) module_name =
654+
match mod_expr.mod_desc with
652655
| Tmod_ident (path, _lident) -> Ident path
653656
| Tmod_structure structure ->
654657
let env = env |> Env.add_module ~name:module_name in
@@ -665,15 +668,23 @@ and for_module ~env mod_desc module_name =
665668
~extent:arg_name.loc ~stamp ~module_path:NotVisible false []
666669
in
667670
Stamps.add_module env.stamps stamp declared);
668-
for_module ~env result_expr.mod_desc module_name
669-
| Tmod_apply (functor_, _arg, _coercion) ->
670-
for_module ~env functor_.mod_desc module_name
671+
for_module ~env result_expr module_name
672+
| Tmod_apply _ ->
673+
(* The functor's body has the parameter's types; the application's
674+
signature has the argument's. Its items keep the identifiers of the
675+
body, whose stamps are already taken, so they are renamed first,
676+
with stamps above any that a compilation hands out. *)
677+
let env = env |> Env.add_module ~name:module_name in
678+
if Ident.current_time () < renamed_stamp_floor then
679+
Ident.set_current_time renamed_stamp_floor;
680+
for_type_module ~name:module_name ~env
681+
(Subst.modtype Subst.identity mod_expr.mod_type)
671682
| Tmod_unpack (_expr, module_type) ->
672683
let env = env |> Env.add_module ~name:module_name in
673684
for_type_module ~name:module_name ~env module_type
674685
| Tmod_constraint (expr, typ, _constraint, _coercion) ->
675686
(* TODO do this better I think *)
676-
let mod_kind = for_module ~env expr.mod_desc module_name in
687+
let mod_kind = for_module ~env expr module_name in
677688
let env = env |> Env.add_module ~name:module_name in
678689
let mod_type_kind = for_type_module ~name:module_name ~env typ in
679690
Constraint (mod_kind, mod_type_kind)
@@ -690,7 +701,7 @@ and scan_let_modules ~env (e : Typedtree.expression) =
690701
(match expression.exp_desc with
691702
| Texp_letmodule (id, name, mexpr, _body) ->
692703
let stamp = Ident.binding_time id in
693-
let item = for_module ~env mexpr.mod_desc name.txt in
704+
let item = for_module ~env mexpr name.txt in
694705
let declared =
695706
Process_attributes.new_declared ~item ~extent:name.loc ~name ~stamp
696707
~module_path:NotVisible false []
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Hover and completion on a module created by applying a functor show the applied types, such as `array<int>`, instead of the functor body's `array<O.t>`.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
module type Ord = {
2+
type t
3+
let compare: (t, t) => int
4+
}
5+
6+
module MakeSet = (O: Ord) => {
7+
type t = array<O.t>
8+
let add = (s: t, x: O.t) => s->Array.concat([x])
9+
}
10+
11+
module IntSet = MakeSet({
12+
type t = int
13+
let compare = (a: int, b) => a - b
14+
})
15+
16+
let s = IntSet.add([], 1)
17+
// ^hov
18+
19+
let t: IntSet.t = []
20+
// ^hov
21+
22+
// let s2 = IntSet.
23+
// ^com
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
Hover src/AppliedFunctor.res 15:15
2+
{
3+
"contents": {
4+
"kind": "markdown",
5+
"value": "```rescript\n(IntSet.t, int) => array<int>\n```\n\n---\n\n```\n \n```\n```rescript\ntype IntSet.t = array<int>\n```\nGo to: [Type definition](AppliedFunctor.res#L7,3)\n"
6+
}
7+
}
8+
9+
Hover src/AppliedFunctor.res 18:14
10+
{
11+
"contents": {
12+
"kind": "markdown",
13+
"value": "```rescript\nIntSet.t\n```\n\n---\n\n```\n \n```\n```rescript\ntype IntSet.t = array<int>\n```\nGo to: [Type definition](AppliedFunctor.res#L7,3)\n"
14+
}
15+
}
16+
17+
Complete src/AppliedFunctor.res 21:19
18+
pos_cursor:[21:19] pos_no_white:[21:18] Found expr:[21:12->21:19]
19+
Pexp_ident IntSet.:[21:12->21:19]
20+
Completable: Cpath Value[IntSet, ""]
21+
Package opens Stdlib.place holder Pervasives.JsxModules.place holder
22+
Resolved opens 1 Stdlib
23+
ContextPath Value[IntSet, ""]
24+
Path IntSet.
25+
[
26+
{
27+
"detail": "(t, int) => array<int>",
28+
"kind": 12,
29+
"label": "add",
30+
"tags": []
31+
}
32+
]
33+

0 commit comments

Comments
 (0)