From 93deefc9e51fa3dfb6b349f0afb0ddf65b969482 Mon Sep 17 00:00:00 2001 From: Christoph Knittel Date: Tue, 27 Oct 2026 12:59:25 +0000 Subject: [PATCH 01/12] 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 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" After (text): tests/analysis_tests/tests/src/AppliedFunctor.res "detail": "(t, int) => array" Diagnostic modified (hover on a type of an applied functor): Before (text): type IntSet.t = array After (text): type IntSet.t = array 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 Claude-Session: https://claude.ai/code/session_01Kbf3gLMawVgtmiKy3TMBc3 Claude-Session: https://claude.ai/code/session_01Kr4cjoUi2f5VpMvE6wT9bo Signed-off-by: Christoph Knittel --- analysis/src/process_cmt.ml | 31 +++++++++++------ changes/applied-functor-analysis-types.fix.md | 1 + .../tests/src/AppliedFunctor.res | 23 +++++++++++++ .../tests/src/expected/AppliedFunctor.res.txt | 33 +++++++++++++++++++ 4 files changed, 78 insertions(+), 10 deletions(-) create mode 100644 changes/applied-functor-analysis-types.fix.md create mode 100644 tests/analysis_tests/tests/src/AppliedFunctor.res create mode 100644 tests/analysis_tests/tests/src/expected/AppliedFunctor.res.txt diff --git a/analysis/src/process_cmt.ml b/analysis/src/process_cmt.ml index 18d854a524..eb36f9f0d9 100644 --- a/analysis/src/process_cmt.ml +++ b/analysis/src/process_cmt.ml @@ -185,6 +185,10 @@ and for_type_module ~name ~env module_type = | Mty_functor (_argIdent, _argType, result_type) -> for_type_module ~name ~env result_type +(* Stamps given to renamed identifiers start here, far above the stamps + in a typed tree, so that they never meet one of them. *) +let renamed_stamp_floor = 1 lsl 40 + let get_module_type_path mod_desc = match mod_desc with | 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) bindings |> List.iter (fun {Typedtree.vb_expr} -> scan_let_modules ~env vb_expr); !items - | Tstr_module - {mb_id; mb_attributes; mb_loc; mb_name = name; mb_expr = {mod_desc}} + | Tstr_module {mb_id; mb_attributes; mb_loc; mb_name = name; mb_expr} when not (String.length name.txt >= 6 && (String.sub name.txt 0 6 = "local_") [@doesNotRaise]) (* %%private generates a dummy module called local_... *) -> - let item = for_module ~env mod_desc name.txt in + let item = for_module ~env mb_expr name.txt in let declared = add_declared ~item ~name ~extent:mb_loc ~stamp:(Ident.binding_time mb_id) ~env mb_attributes @@ -647,8 +650,8 @@ let rec for_structure_item ~(env : Shared_types.Env.t) ~(exported : Exported.t) decl |> for_type_declaration ~env ~exported ~rec_status) | _ -> [] -and for_module ~env mod_desc module_name = - match mod_desc with +and for_module ~env (mod_expr : Typedtree.module_expr) module_name = + match mod_expr.mod_desc with | Tmod_ident (path, _lident) -> Ident path | Tmod_structure structure -> let env = env |> Env.add_module ~name:module_name in @@ -665,15 +668,23 @@ and for_module ~env mod_desc module_name = ~extent:arg_name.loc ~stamp ~module_path:NotVisible false [] in Stamps.add_module env.stamps stamp declared); - for_module ~env result_expr.mod_desc module_name - | Tmod_apply (functor_, _arg, _coercion) -> - for_module ~env functor_.mod_desc module_name + for_module ~env result_expr module_name + | Tmod_apply _ -> + (* The functor's body has the parameter's types; the application's + signature has the argument's. Its items keep the identifiers of the + body, whose stamps are already taken, so they are renamed first, + with stamps above any that a compilation hands out. *) + let env = env |> Env.add_module ~name:module_name in + if Ident.current_time () < renamed_stamp_floor then + Ident.set_current_time renamed_stamp_floor; + for_type_module ~name:module_name ~env + (Subst.modtype Subst.identity mod_expr.mod_type) | Tmod_unpack (_expr, module_type) -> let env = env |> Env.add_module ~name:module_name in for_type_module ~name:module_name ~env module_type | Tmod_constraint (expr, typ, _constraint, _coercion) -> (* TODO do this better I think *) - let mod_kind = for_module ~env expr.mod_desc module_name in + let mod_kind = for_module ~env expr module_name in let env = env |> Env.add_module ~name:module_name in let mod_type_kind = for_type_module ~name:module_name ~env typ in Constraint (mod_kind, mod_type_kind) @@ -690,7 +701,7 @@ and scan_let_modules ~env (e : Typedtree.expression) = (match expression.exp_desc with | Texp_letmodule (id, name, mexpr, _body) -> let stamp = Ident.binding_time id in - let item = for_module ~env mexpr.mod_desc name.txt in + let item = for_module ~env mexpr name.txt in let declared = Process_attributes.new_declared ~item ~extent:name.loc ~name ~stamp ~module_path:NotVisible false [] diff --git a/changes/applied-functor-analysis-types.fix.md b/changes/applied-functor-analysis-types.fix.md new file mode 100644 index 0000000000..2905eaf477 --- /dev/null +++ b/changes/applied-functor-analysis-types.fix.md @@ -0,0 +1 @@ +- Hover and completion on a module created by applying a functor show the applied types, such as `array`, instead of the functor body's `array`. diff --git a/tests/analysis_tests/tests/src/AppliedFunctor.res b/tests/analysis_tests/tests/src/AppliedFunctor.res new file mode 100644 index 0000000000..4df7ac2152 --- /dev/null +++ b/tests/analysis_tests/tests/src/AppliedFunctor.res @@ -0,0 +1,23 @@ +module type Ord = { + type t + let compare: (t, t) => int +} + +module MakeSet = (O: Ord) => { + type t = array + let add = (s: t, x: O.t) => s->Array.concat([x]) +} + +module IntSet = MakeSet({ + type t = int + let compare = (a: int, b) => a - b +}) + +let s = IntSet.add([], 1) +// ^hov + +let t: IntSet.t = [] +// ^hov + +// let s2 = IntSet. +// ^com diff --git a/tests/analysis_tests/tests/src/expected/AppliedFunctor.res.txt b/tests/analysis_tests/tests/src/expected/AppliedFunctor.res.txt new file mode 100644 index 0000000000..739541fd1b --- /dev/null +++ b/tests/analysis_tests/tests/src/expected/AppliedFunctor.res.txt @@ -0,0 +1,33 @@ +Hover src/AppliedFunctor.res 15:15 +{ + "contents": { + "kind": "markdown", + "value": "```rescript\n(IntSet.t, int) => array\n```\n\n---\n\n```\n \n```\n```rescript\ntype IntSet.t = array\n```\nGo to: [Type definition](AppliedFunctor.res#L7,3)\n" + } +} + +Hover src/AppliedFunctor.res 18:14 +{ + "contents": { + "kind": "markdown", + "value": "```rescript\nIntSet.t\n```\n\n---\n\n```\n \n```\n```rescript\ntype IntSet.t = array\n```\nGo to: [Type definition](AppliedFunctor.res#L7,3)\n" + } +} + +Complete src/AppliedFunctor.res 21:19 +pos_cursor:[21:19] pos_no_white:[21:18] Found expr:[21:12->21:19] +Pexp_ident IntSet.:[21:12->21:19] +Completable: Cpath Value[IntSet, ""] +Package opens Stdlib.place holder Pervasives.JsxModules.place holder +Resolved opens 1 Stdlib +ContextPath Value[IntSet, ""] +Path IntSet. +[ + { + "detail": "(t, int) => array", + "kind": 12, + "label": "add", + "tags": [] + } +] + From d45f713da3bf2030e20b9adaa7f1f0b73abb54d3 Mon Sep 17 00:00:00 2001 From: Christoph Knittel Date: Tue, 27 Oct 2026 17:45:53 +0000 Subject: [PATCH 02/12] Keep the functor's file for members of an applied functor Codex review on #9084: the members of `module M = Other.Make(X)` were registered in the current file with the locations of Other's functor body, so Go to Definition and hover's type-definition link combined Other's location with the current file. Their module path now records the functor, as it does for included modules, so both resolve to the functor's file. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Kbf3gLMawVgtmiKy3TMBc3 Claude-Session: https://claude.ai/code/session_01Kr4cjoUi2f5VpMvE6wT9bo Signed-off-by: Christoph Knittel --- analysis/src/hover.ml | 17 +++++++++++++---- analysis/src/markdown.ml | 9 +++++++-- analysis/src/process_cmt.ml | 12 ++++++++++-- analysis/src/signature_help.ml | 2 +- .../tests/src/AppliedFunctorUse.res | 10 ++++++++++ .../src/expected/AppliedFunctorUse.res.txt | 17 +++++++++++++++++ 6 files changed, 58 insertions(+), 9 deletions(-) create mode 100644 tests/analysis_tests/tests/src/AppliedFunctorUse.res create mode 100644 tests/analysis_tests/tests/src/expected/AppliedFunctorUse.res.txt diff --git a/analysis/src/hover.ml b/analysis/src/hover.ml index 4abdb4a490..2107500979 100644 --- a/analysis/src/hover.ml +++ b/analysis/src/hover.ml @@ -64,6 +64,9 @@ type extracted_type = { decl: Types.type_declaration; env: Shared_types.Query_env.t; loc: Warnings.loc; + uri: Uri.t; + (** the file of [loc], which differs from [env]'s for a type + of an applied or included module of another file *) } let find_relevant_types_from_type ~state ~file ~package typ = @@ -103,9 +106,13 @@ let find_relevant_types_from_type ~state ~file ~package typ = let from_constructor_path ~env path = match References.dig_constructor ~state ~env ~package path with | None -> None - | Some (env, {name = {txt}; extent_loc; item = {decl}}) -> + | Some (env, {name = {txt}; extent_loc; item = {decl}; module_path}) -> if Utils.is_uncurried_internal path then None - else Some {name = txt; env; loc = extent_loc; decl; path} + else + let uri = + Resolve_path.get_source_uri ~state ~env ~package module_path + in + Some {name = txt; env; loc = extent_loc; decl; path; uri} in let constructors = Shared.find_type_constructors types_to_search in constructors |> List.filter_map (from_constructor_path ~env:env_to_search) @@ -142,12 +149,14 @@ let expand_types ~state ~file ~package ~supports_markdown_links typ = else ( types_seen := String_set.add type_id !types_seen; true)) - |> List.map (fun {decl; env; loc; path} -> + |> List.map (fun {decl; env; loc; path; uri} -> let link_to_type_definition_str = if supports_markdown_links && not (is_inline_record_definition decl path) - then Markdown.go_to_definition_text ~env ~pos:loc.Warnings.loc_start + then + Markdown.go_to_definition_text ~uri ~env + ~pos:loc.Warnings.loc_start () else "" in Markdown.divider diff --git a/analysis/src/markdown.ml b/analysis/src/markdown.ml index b5254b9b25..a5ba3f0ce0 100644 --- a/analysis/src/markdown.ml +++ b/analysis/src/markdown.ml @@ -8,12 +8,17 @@ let make_goto_command link = Printf.sprintf "[%s](%s#L%d,%d)" link.label link.file (link.start_pos.line + 1) (link.start_pos.character + 1) -let go_to_definition_text ~env ~pos = +let go_to_definition_text ?uri ~env ~pos () = let start_line, start_col = Pos.of_lexing pos in + let uri = + match uri with + | Some uri -> uri + | None -> env.Shared_types.Query_env.file.uri + in "\nGo to: " ^ make_goto_command { label = "Type definition"; - file = Uri.to_string env.Shared_types.Query_env.file.uri; + file = Uri.to_string uri; start_pos = {line = start_line; character = start_col}; } diff --git a/analysis/src/process_cmt.ml b/analysis/src/process_cmt.ml index eb36f9f0d9..9a2ac8b83f 100644 --- a/analysis/src/process_cmt.ml +++ b/analysis/src/process_cmt.ml @@ -669,12 +669,20 @@ and for_module ~env (mod_expr : Typedtree.module_expr) module_name = in Stamps.add_module env.stamps stamp declared); for_module ~env result_expr module_name - | Tmod_apply _ -> + | Tmod_apply (functor_, _arg, _coercion) -> (* The functor's body has the parameter's types; the application's signature has the argument's. Its items keep the identifiers of the body, whose stamps are already taken, so they are renamed first, - with stamps above any that a compilation hands out. *) + with stamps above any that a compilation hands out. Their locations + are in the functor's body, so they are marked as coming from the + functor, like included items, for definitions to find its file. *) let env = env |> Env.add_module ~name:module_name in + let env = + match get_module_path functor_.mod_desc with + | Some path -> + {env with module_path = IncludedModule (path, env.module_path)} + | None -> env + in if Ident.current_time () < renamed_stamp_floor then Ident.set_current_time renamed_stamp_floor; for_type_module ~name:module_name ~env diff --git a/analysis/src/signature_help.ml b/analysis/src/signature_help.ml index 3462855f54..d14d5e3991 100644 --- a/analysis/src/signature_help.ml +++ b/analysis/src/signature_help.ml @@ -12,7 +12,7 @@ let docs_for_label type_expr ~file ~state ~package ~supports_markdown_links = |> List.map (fun {Hover.decl; name; env; loc; path} -> let link_to_type_definition_str = if supports_markdown_links then - Markdown.go_to_definition_text ~env ~pos:loc.Warnings.loc_start + Markdown.go_to_definition_text ~env ~pos:loc.Warnings.loc_start () else "" in (* Since printing the whole name via its path can get quite long, and diff --git a/tests/analysis_tests/tests/src/AppliedFunctorUse.res b/tests/analysis_tests/tests/src/AppliedFunctorUse.res new file mode 100644 index 0000000000..c5dc4e0d3b --- /dev/null +++ b/tests/analysis_tests/tests/src/AppliedFunctorUse.res @@ -0,0 +1,10 @@ +module StrSet = AppliedFunctor.MakeSet({ + type t = string + let compare = (a: string, b) => a < b ? -1 : a > b ? 1 : 0 +}) + +let s = StrSet.add([], "a") +// ^def + +let t: StrSet.t = [] +// ^hov diff --git a/tests/analysis_tests/tests/src/expected/AppliedFunctorUse.res.txt b/tests/analysis_tests/tests/src/expected/AppliedFunctorUse.res.txt new file mode 100644 index 0000000000..08c315a352 --- /dev/null +++ b/tests/analysis_tests/tests/src/expected/AppliedFunctorUse.res.txt @@ -0,0 +1,17 @@ +Definition src/AppliedFunctorUse.res 5:15 +{ + "range": { + "end": { "character": 9, "line": 7 }, + "start": { "character": 6, "line": 7 } + }, + "uri": "file:///AppliedFunctor.res" +} + +Hover src/AppliedFunctorUse.res 8:14 +{ + "contents": { + "kind": "markdown", + "value": "```rescript\nStrSet.t\n```\n\n---\n\n```\n \n```\n```rescript\ntype StrSet.t = array\n```\nGo to: [Type definition](AppliedFunctor.res#L7,3)\n" + } +} + From 467cb2a828ca7ee397dd9004e60b04f517172427 Mon Sep 17 00:00:00 2001 From: Christoph Knittel Date: Tue, 27 Oct 2026 20:22:05 +0000 Subject: [PATCH 03/12] Link signature help type definitions to their declaring file Codex review on #9084: signature help built its Type definition link from the caller's file, so for a type of an applied functor from another file it pointed into the caller. It now takes the file from the type's declaration, as hover does; the link function requires it, so no caller can fall back to the wrong file. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Kbf3gLMawVgtmiKy3TMBc3 Claude-Session: https://claude.ai/code/session_01Kr4cjoUi2f5VpMvE6wT9bo Signed-off-by: Christoph Knittel --- analysis/src/hover.ml | 6 +-- analysis/src/markdown.ml | 7 +--- analysis/src/signature_help.ml | 4 +- .../tests/src/AppliedFunctorUse.res | 3 ++ .../src/expected/AppliedFunctorUse.res.txt | 37 +++++++++++++++++++ 5 files changed, 45 insertions(+), 12 deletions(-) diff --git a/analysis/src/hover.ml b/analysis/src/hover.ml index 2107500979..a4b0bca851 100644 --- a/analysis/src/hover.ml +++ b/analysis/src/hover.ml @@ -149,14 +149,12 @@ let expand_types ~state ~file ~package ~supports_markdown_links typ = else ( types_seen := String_set.add type_id !types_seen; true)) - |> List.map (fun {decl; env; loc; path; uri} -> + |> List.map (fun {decl; loc; path; uri} -> let link_to_type_definition_str = if supports_markdown_links && not (is_inline_record_definition decl path) - then - Markdown.go_to_definition_text ~uri ~env - ~pos:loc.Warnings.loc_start () + then Markdown.go_to_definition_text ~uri ~pos:loc.Warnings.loc_start else "" in Markdown.divider diff --git a/analysis/src/markdown.ml b/analysis/src/markdown.ml index a5ba3f0ce0..2df30561eb 100644 --- a/analysis/src/markdown.ml +++ b/analysis/src/markdown.ml @@ -8,13 +8,8 @@ let make_goto_command link = Printf.sprintf "[%s](%s#L%d,%d)" link.label link.file (link.start_pos.line + 1) (link.start_pos.character + 1) -let go_to_definition_text ?uri ~env ~pos () = +let go_to_definition_text ~uri ~pos = let start_line, start_col = Pos.of_lexing pos in - let uri = - match uri with - | Some uri -> uri - | None -> env.Shared_types.Query_env.file.uri - in "\nGo to: " ^ make_goto_command { diff --git a/analysis/src/signature_help.ml b/analysis/src/signature_help.ml index d14d5e3991..0f25de5f2d 100644 --- a/analysis/src/signature_help.ml +++ b/analysis/src/signature_help.ml @@ -9,10 +9,10 @@ let docs_for_label type_expr ~file ~state ~package ~supports_markdown_links = let type_names = types |> List.map (fun {Hover.name} -> name) in let type_definitions = types - |> List.map (fun {Hover.decl; name; env; loc; path} -> + |> List.map (fun {Hover.decl; name; loc; path; uri} -> let link_to_type_definition_str = if supports_markdown_links then - Markdown.go_to_definition_text ~env ~pos:loc.Warnings.loc_start () + Markdown.go_to_definition_text ~uri ~pos:loc.Warnings.loc_start else "" in (* Since printing the whole name via its path can get quite long, and diff --git a/tests/analysis_tests/tests/src/AppliedFunctorUse.res b/tests/analysis_tests/tests/src/AppliedFunctorUse.res index c5dc4e0d3b..c85a2efddf 100644 --- a/tests/analysis_tests/tests/src/AppliedFunctorUse.res +++ b/tests/analysis_tests/tests/src/AppliedFunctorUse.res @@ -8,3 +8,6 @@ let s = StrSet.add([], "a") let t: StrSet.t = [] // ^hov + +// let _ = StrSet.add( +// ^she diff --git a/tests/analysis_tests/tests/src/expected/AppliedFunctorUse.res.txt b/tests/analysis_tests/tests/src/expected/AppliedFunctorUse.res.txt index 08c315a352..459216d64a 100644 --- a/tests/analysis_tests/tests/src/expected/AppliedFunctorUse.res.txt +++ b/tests/analysis_tests/tests/src/expected/AppliedFunctorUse.res.txt @@ -15,3 +15,40 @@ Hover src/AppliedFunctorUse.res 8:14 } } +Signature help src/AppliedFunctorUse.res 11:22 +pos_cursor:[11:21] pos_no_white:[11:20] Found expr:[11:11->11:22] +Pexp_apply ...[11:11->11:21] (...[14:0->11:22]) +pos_cursor:[11:21] pos_no_white:[11:20] Found expr:[11:11->11:21] +Pexp_ident StrSet.add:[11:11->11:21] +Completable: Cpath Value[StrSet, add] +Package opens Stdlib.place holder Pervasives.JsxModules.place holder +Resolved opens 1 Stdlib +ContextPath Value[StrSet, add] +Path StrSet.add +arg_at_cursor: unlabelled<0> +extracted params: +[t, string] +{ + "activeParameter": 0, + "activeSignature": 0, + "signatures": [ + { + "activeParameter": 0, + "label": "(t, string) => array", + "parameters": [ + { + "documentation": { + "kind": "markdown", + "value": "```rescript\ntype t = array\n```\nGo to: [Type definition](AppliedFunctor.res#L7,3)" + }, + "label": [ 1, 2 ] + }, + { + "documentation": { "kind": "markdown", "value": "" }, + "label": [ 4, 10 ] + } + ] + } + ] +} + From 4df37210068c46f7c6be6449b9861c84cdd92e27 Mon Sep 17 00:00:00 2001 From: Christoph Knittel Date: Tue, 27 Oct 2026 20:22:05 +0000 Subject: [PATCH 04/12] Connect renamed members of an applied functor to their declarations Codex review on #9084: the members of an applied functor are registered under fresh stamps, so references to `IntSet.add` were no longer references to `MakeSet.add`; Find References missed them and renaming the declaration left them unchanged. Each renamed stamp now maps to the stamp it was renamed from: a use of the member is also recorded as a use of the original, and references starting from the member start from the original. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Kbf3gLMawVgtmiKy3TMBc3 Claude-Session: https://claude.ai/code/session_01Kr4cjoUi2f5VpMvE6wT9bo Signed-off-by: Christoph Knittel --- analysis/src/process_cmt.ml | 76 ++++++++++-- analysis/src/process_extra.ml | 12 +- analysis/src/references.ml | 5 + analysis/src/shared_types.ml | 10 +- .../tests/src/AppliedFunctor.res | 7 ++ .../tests/src/expected/AppliedFunctor.res.txt | 111 +++++++++++++++++- 6 files changed, 206 insertions(+), 15 deletions(-) diff --git a/analysis/src/process_cmt.ml b/analysis/src/process_cmt.ml index 9a2ac8b83f..850d7d4671 100644 --- a/analysis/src/process_cmt.ml +++ b/analysis/src/process_cmt.ml @@ -189,6 +189,39 @@ and for_type_module ~name ~env module_type = in a typed tree, so that they never meet one of them. *) let renamed_stamp_floor = 1 lsl 40 +(* [Subst] renames the items of a signature in place, so the original and + the renamed signature list the same items in the same order. Records + each renamed stamp's original, nested signatures included, for + references to find the declarations in the functor body. *) +let rec record_renamed_stamps table (original : Types.module_type) + (renamed : Types.module_type) = + match (original, renamed) with + | Mty_signature items, Mty_signature renamed_items + when List.length items = List.length renamed_items -> + List.iter2 + (fun (item : Types.signature_item) (renamed_item : Types.signature_item) + -> + let ids = + match (item, renamed_item) with + | Sig_value (id, _), Sig_value (id', _) + | Sig_type (id, _, _), Sig_type (id', _, _) + | Sig_typext (id, _, _), Sig_typext (id', _, _) + | Sig_modtype (id, _), Sig_modtype (id', _) -> + Some (id, id') + | Sig_module (id, md, _), Sig_module (id', md', _) -> + record_renamed_stamps table md.md_type md'.md_type; + Some (id, id') + | _ -> None + in + match ids with + | Some (id, id') -> + Hashtbl.replace table (Ident.binding_time id') (Ident.binding_time id) + | None -> ()) + items renamed_items + | Mty_functor (_, _, result), Mty_functor (_, _, renamed_result) -> + record_renamed_stamps table result renamed_result + | _ -> () + let get_module_type_path mod_desc = match mod_desc with | Typedtree.Tmty_ident (path, _) | Tmty_alias (path, _) -> Some path @@ -685,8 +718,9 @@ and for_module ~env (mod_expr : Typedtree.module_expr) module_name = in if Ident.current_time () < renamed_stamp_floor then Ident.set_current_time renamed_stamp_floor; - for_type_module ~name:module_name ~env - (Subst.modtype Subst.identity mod_expr.mod_type) + let renamed = Subst.modtype Subst.identity mod_expr.mod_type in + record_renamed_stamps env.renamed_stamps mod_expr.mod_type renamed; + for_type_module ~name:module_name ~env renamed | Tmod_unpack (_expr, module_type) -> let env = env |> Env.add_module ~name:module_name in for_type_module ~name:module_name ~env module_type @@ -742,7 +776,11 @@ and for_structure ~name ~env str_items = let file_for_cmt_infos ~module_name ~uri ({cmt_modname; cmt_annots} : Cmt_format.cmt_infos) = let env = - {Env.stamps = Stamps.init (); module_path = File (uri, module_name)} + { + Env.stamps = Stamps.init (); + module_path = File (uri, module_name); + renamed_stamps = Hashtbl.create 1; + } in match cmt_annots with | Partial_implementation parts -> @@ -756,7 +794,13 @@ let file_for_cmt_infos ~module_name ~uri |> List.concat in let structure = for_structure ~name:module_name ~env items in - {File.uri; module_name = cmt_modname; stamps = env.stamps; structure} + { + File.uri; + module_name = cmt_modname; + stamps = env.stamps; + structure; + renamed_stamps = env.renamed_stamps; + } | Partial_interface parts -> let items = parts |> Array.to_list @@ -768,13 +812,31 @@ let file_for_cmt_infos ~module_name ~uri |> List.concat in let structure = for_signature ~name:module_name ~env items in - {uri; module_name = cmt_modname; stamps = env.stamps; structure} + { + uri; + module_name = cmt_modname; + stamps = env.stamps; + structure; + renamed_stamps = env.renamed_stamps; + } | Implementation structure -> let structure = for_structure ~name:module_name ~env structure.str_items in - {uri; module_name = cmt_modname; stamps = env.stamps; structure} + { + uri; + module_name = cmt_modname; + stamps = env.stamps; + structure; + renamed_stamps = env.renamed_stamps; + } | Interface signature -> let structure = for_signature ~name:module_name ~env signature.sig_items in - {uri; module_name = cmt_modname; stamps = env.stamps; structure} + { + uri; + module_name = cmt_modname; + stamps = env.stamps; + structure; + renamed_stamps = env.renamed_stamps; + } let file_for_cmt ~state ~module_name ~cmt ~uri = match Hashtbl.find_opt state.cmt_cache cmt with diff --git a/analysis/src/process_extra.ml b/analysis/src/process_extra.ml index 4f14dc3136..f93be845d0 100644 --- a/analysis/src/process_extra.ml +++ b/analysis/src/process_extra.ml @@ -135,13 +135,21 @@ let extra_for_cmt ~(iterator : Tast_iterator.iterator) extra_for_signature_items ~iterator items; extra_for_parts parts +(* A use of an applied functor's member is also a use of the declaration + in the functor's body that it was renamed from. *) +let add_reference_with_original ~(env : Query_env.t) ~extra stamp loc = + add_reference ~extra stamp loc; + match Hashtbl.find_opt env.file.renamed_stamps stamp with + | Some original -> add_reference ~extra original loc + | None -> () + let add_for_path ~env ~extra path lident loc typ tip = let ident_name = Longident.last lident in let ident_loc = Utils.end_of_location loc (String.length ident_name) in let loc_type = match Resolve_path.from_compiler_path ~env path with | Stamp stamp -> - add_reference ~extra stamp ident_loc; + add_reference_with_original ~env ~extra stamp ident_loc; LocalReference (stamp, tip) | NotFound -> NotFound | Global (module_name, path) -> @@ -154,7 +162,7 @@ let add_for_path ~env ~extra path lident loc typ tip = | _ -> Exported.find env.exported Exported.Value name with | Some stamp -> - add_reference ~extra stamp ident_loc; + add_reference_with_original ~env ~extra stamp ident_loc; LocalReference (stamp, tip) | None -> NotFound) | GlobalMod _ -> NotFound diff --git a/analysis/src/references.ml b/analysis/src/references.ml index a0da5bc553..d23f0bd34d 100644 --- a/analysis/src/references.ml +++ b/analysis/src/references.ml @@ -440,6 +440,11 @@ type references = { let for_local_stamp ~state ~full:{file; extra; package} stamp (tip : Tip.t) = let env = Query_env.from_file file in + let stamp = + match Hashtbl.find_opt file.renamed_stamps stamp with + | Some original -> original + | None -> stamp + in match match tip with | Constructor name -> diff --git a/analysis/src/shared_types.ml b/analysis/src/shared_types.ml index 5333f40832..d23303fe4a 100644 --- a/analysis/src/shared_types.ml +++ b/analysis/src/shared_types.ml @@ -274,6 +274,7 @@ module File = struct stamps: Stamps.t; module_name: string; structure: Module.structure; + renamed_stamps: (int, int) Hashtbl.t; } let create module_name uri = @@ -281,6 +282,7 @@ module File = struct uri; stamps = Stamps.init (); module_name; + renamed_stamps = Hashtbl.create 1; structure = { name = module_name; @@ -404,7 +406,13 @@ and completion_type = } module Env = struct - type t = {stamps: Stamps.t; module_path: Module_path.t} + type t = { + stamps: Stamps.t; + module_path: Module_path.t; + renamed_stamps: (int, int) Hashtbl.t; + (** the stamps of an applied functor's renamed members, mapped to + those of the functor body's declarations they come from *) + } let add_exported_module ~name ~is_type env = { env with diff --git a/tests/analysis_tests/tests/src/AppliedFunctor.res b/tests/analysis_tests/tests/src/AppliedFunctor.res index 4df7ac2152..04dc27ae96 100644 --- a/tests/analysis_tests/tests/src/AppliedFunctor.res +++ b/tests/analysis_tests/tests/src/AppliedFunctor.res @@ -6,6 +6,7 @@ module type Ord = { module MakeSet = (O: Ord) => { type t = array let add = (s: t, x: O.t) => s->Array.concat([x]) + // ^ref } module IntSet = MakeSet({ @@ -16,6 +17,12 @@ module IntSet = MakeSet({ let s = IntSet.add([], 1) // ^hov +let s3 = IntSet.add([], 3) +// ^ref + +let s4 = IntSet.add([], 4) +// ^ren insert + let t: IntSet.t = [] // ^hov diff --git a/tests/analysis_tests/tests/src/expected/AppliedFunctor.res.txt b/tests/analysis_tests/tests/src/expected/AppliedFunctor.res.txt index 739541fd1b..f701e835ff 100644 --- a/tests/analysis_tests/tests/src/expected/AppliedFunctor.res.txt +++ b/tests/analysis_tests/tests/src/expected/AppliedFunctor.res.txt @@ -1,4 +1,36 @@ -Hover src/AppliedFunctor.res 15:15 +References src/AppliedFunctor.res 7:7 +[ + { + "range": { + "end": { "character": 9, "line": 7 }, + "start": { "character": 6, "line": 7 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 18, "line": 16 }, + "start": { "character": 15, "line": 16 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 19, "line": 19 }, + "start": { "character": 16, "line": 19 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 19, "line": 22 }, + "start": { "character": 16, "line": 22 } + }, + "uri": "file:///AppliedFunctor.res" + } +] + +Hover src/AppliedFunctor.res 16:15 { "contents": { "kind": "markdown", @@ -6,7 +38,76 @@ Hover src/AppliedFunctor.res 15:15 } } -Hover src/AppliedFunctor.res 18:14 +References src/AppliedFunctor.res 19:16 +[ + { + "range": { + "end": { "character": 9, "line": 7 }, + "start": { "character": 6, "line": 7 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 18, "line": 16 }, + "start": { "character": 15, "line": 16 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 19, "line": 19 }, + "start": { "character": 16, "line": 19 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 19, "line": 22 }, + "start": { "character": 16, "line": 22 } + }, + "uri": "file:///AppliedFunctor.res" + } +] + +Rename src/AppliedFunctor.res 22:16 insert +[ + { + "edits": [ + { + "newText": "insert", + "range": { + "end": { "character": 9, "line": 7 }, + "start": { "character": 6, "line": 7 } + } + }, + { + "newText": "insert", + "range": { + "end": { "character": 18, "line": 16 }, + "start": { "character": 15, "line": 16 } + } + }, + { + "newText": "insert", + "range": { + "end": { "character": 19, "line": 19 }, + "start": { "character": 16, "line": 19 } + } + }, + { + "newText": "insert", + "range": { + "end": { "character": 19, "line": 22 }, + "start": { "character": 16, "line": 22 } + } + } + ], + "textDocument": { "uri": "file:///AppliedFunctor.res", "version": 0 } + } +] + +Hover src/AppliedFunctor.res 25:14 { "contents": { "kind": "markdown", @@ -14,9 +115,9 @@ Hover src/AppliedFunctor.res 18:14 } } -Complete src/AppliedFunctor.res 21:19 -pos_cursor:[21:19] pos_no_white:[21:18] Found expr:[21:12->21:19] -Pexp_ident IntSet.:[21:12->21:19] +Complete src/AppliedFunctor.res 28:19 +pos_cursor:[28:19] pos_no_white:[28:18] Found expr:[28:12->28:19] +Pexp_ident IntSet.:[28:12->28:19] Completable: Cpath Value[IntSet, ""] Package opens Stdlib.place holder Pervasives.JsxModules.place holder Resolved opens 1 Stdlib From 9bf663d2ffb00990d51f4c110e010cbd944995c0 Mon Sep 17 00:00:00 2001 From: Christoph Knittel Date: Tue, 27 Oct 2026 21:23:08 +0000 Subject: [PATCH 05/12] Connect nested modules of an applied functor to their declarations Codex review on #9084: uses of a module path such as `M.Nested` were recorded only under the renamed stamp. They are now also recorded under the original, like value uses. The originals are taken from the functor's result signature: when the argument is a path, the application's signature was already renamed by the compiler and doesn't carry the body's stamps. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Kbf3gLMawVgtmiKy3TMBc3 Claude-Session: https://claude.ai/code/session_01Kr4cjoUi2f5VpMvE6wT9bo Signed-off-by: Christoph Knittel --- analysis/src/process_cmt.ml | 10 +++++- analysis/src/process_extra.ml | 4 +-- .../tests/src/AppliedFunctor.res | 17 +++++++++ .../tests/src/expected/AppliedFunctor.res.txt | 36 +++++++++++++++++++ 4 files changed, 64 insertions(+), 3 deletions(-) diff --git a/analysis/src/process_cmt.ml b/analysis/src/process_cmt.ml index 850d7d4671..1a7763ca94 100644 --- a/analysis/src/process_cmt.ml +++ b/analysis/src/process_cmt.ml @@ -719,7 +719,15 @@ and for_module ~env (mod_expr : Typedtree.module_expr) module_name = if Ident.current_time () < renamed_stamp_floor then Ident.set_current_time renamed_stamp_floor; let renamed = Subst.modtype Subst.identity mod_expr.mod_type in - record_renamed_stamps env.renamed_stamps mod_expr.mod_type renamed; + (* The functor's result signature has the identifiers of the body's + declarations; the application's has them only when the argument is + not a path, else the compiler already renamed them. *) + let functor_result = + match functor_.mod_type with + | Mty_functor (_, _, result) -> result + | mty -> mty + in + record_renamed_stamps env.renamed_stamps functor_result renamed; for_type_module ~name:module_name ~env renamed | Tmod_unpack (_expr, module_type) -> let env = env |> Env.add_module ~name:module_name in diff --git a/analysis/src/process_extra.ml b/analysis/src/process_extra.ml index f93be845d0..86d0898b94 100644 --- a/analysis/src/process_extra.ml +++ b/analysis/src/process_extra.ml @@ -176,7 +176,7 @@ let add_for_path_parent ~env ~extra path loc = add_file_reference ~extra module_name loc; TopLevelModule module_name | Stamp stamp -> - add_reference ~extra stamp loc; + add_reference_with_original ~env ~extra stamp loc; LModule (LocalReference (stamp, Module)) | NotFound -> LModule NotFound | Global (module_name, path) -> @@ -185,7 +185,7 @@ let add_for_path_parent ~env ~extra path loc = | Exported (env, name) -> ( match Exported.find env.exported Exported.Module name with | Some stamp -> - add_reference ~extra stamp loc; + add_reference_with_original ~env ~extra stamp loc; LModule (LocalReference (stamp, Module)) | None -> LModule NotFound) in diff --git a/tests/analysis_tests/tests/src/AppliedFunctor.res b/tests/analysis_tests/tests/src/AppliedFunctor.res index 04dc27ae96..a442c375de 100644 --- a/tests/analysis_tests/tests/src/AppliedFunctor.res +++ b/tests/analysis_tests/tests/src/AppliedFunctor.res @@ -28,3 +28,20 @@ let t: IntSet.t = [] // let s2 = IntSet. // ^com + +module WithNested = (O: Ord) => { + module Nested = { + // ^ref + let value = 1 + } +} + +module IntOrd = { + type t = int + let compare = (a: int, b) => a - b +} + +module N = WithNested(IntOrd) + +let n = N.Nested.value +// ^ref diff --git a/tests/analysis_tests/tests/src/expected/AppliedFunctor.res.txt b/tests/analysis_tests/tests/src/expected/AppliedFunctor.res.txt index f701e835ff..04023d5638 100644 --- a/tests/analysis_tests/tests/src/expected/AppliedFunctor.res.txt +++ b/tests/analysis_tests/tests/src/expected/AppliedFunctor.res.txt @@ -132,3 +132,39 @@ Path IntSet. } ] +References src/AppliedFunctor.res 32:11 +[ + { + "range": { + "end": { "character": 15, "line": 32 }, + "start": { "character": 9, "line": 32 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 16, "line": 45 }, + "start": { "character": 10, "line": 45 } + }, + "uri": "file:///AppliedFunctor.res" + } +] + +References src/AppliedFunctor.res 45:10 +[ + { + "range": { + "end": { "character": 15, "line": 32 }, + "start": { "character": 9, "line": 32 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 16, "line": 45 }, + "start": { "character": 10, "line": 45 } + }, + "uri": "file:///AppliedFunctor.res" + } +] + From 29cad751d67067aea07a2dfafcf0f6d6c303200e Mon Sep 17 00:00:00 2001 From: Christoph Knittel Date: Wed, 28 Oct 2026 07:45:13 +0000 Subject: [PATCH 06/12] Resolve applied fields and constructors to the functor's declarations Codex review on #9084: an applied functor's record fields and constructors are read from its signature, where they have no location. Go to Definition returned that missing location, and rename added it as a reference, producing edits at character -1. Their lookups now go through the type they were renamed from, which has the body's locations, and fields and constructors without a location are not registered as references. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Kbf3gLMawVgtmiKy3TMBc3 Claude-Session: https://claude.ai/code/session_01Kr4cjoUi2f5VpMvE6wT9bo Signed-off-by: Christoph Knittel --- analysis/src/process_extra.ml | 5 ++ analysis/src/references.ml | 11 +++- .../tests/src/AppliedFunctor.res | 17 ++++++ .../tests/src/expected/AppliedFunctor.res.txt | 55 +++++++++++++++++++ 4 files changed, 86 insertions(+), 2 deletions(-) diff --git a/analysis/src/process_extra.ml b/analysis/src/process_extra.ml index 86d0898b94..37c654ae96 100644 --- a/analysis/src/process_extra.ml +++ b/analysis/src/process_extra.ml @@ -29,14 +29,19 @@ let extra_for_file ~(file : File.t) = (TypeDefinition (d.name.txt, d.item.Type.decl, stamp)); add_reference ~extra stamp d.name.loc; match d.item.Type.kind with + (* Fields and constructors of a type read from a signature, such as + an applied functor's, have no location of their own; references + to them are those of the declarations they come from. *) | Record labels -> labels + |> List.filter (fun {fname} -> not fname.loc.loc_ghost) |> List.iter (fun {stamp; fname; typ} -> add_reference ~extra stamp fname.loc; add_loc_item extra fname.loc (Typed (d.name.txt, typ, Definition (d.stamp, Field fname.txt)))) | Variant constructors -> constructors + |> List.filter (fun {Constructor.cname} -> not cname.loc.loc_ghost) |> List.iter (fun {Constructor.stamp; cname} -> add_reference ~extra stamp cname.loc; let t = diff --git a/analysis/src/references.ml b/analysis/src/references.ml index d23f0bd34d..a6c94e4a1c 100644 --- a/analysis/src/references.ml +++ b/analysis/src/references.ml @@ -124,8 +124,15 @@ let declared_for_tip ~(stamps : Stamps.t) stamp (tip : Tip.t) = Stamps.find_module stamps stamp |> Option.map (fun x -> {x with Declared.item = ()}) +(* The type a renamed type of an applied functor comes from, whose fields + and constructors have source locations. *) +let original_type_stamp (file : File.t) stamp = + match Hashtbl.find_opt file.renamed_stamps stamp with + | Some original -> original + | None -> stamp + let get_field (file : File.t) stamp name = - match Stamps.find_type file.stamps stamp with + match Stamps.find_type file.stamps (original_type_stamp file stamp) with | None -> None | Some {item = {kind}} -> ( match kind with @@ -133,7 +140,7 @@ let get_field (file : File.t) stamp name = | _ -> None) let get_constructor (file : File.t) stamp name = - match Stamps.find_type file.stamps stamp with + match Stamps.find_type file.stamps (original_type_stamp file stamp) with | None -> None | Some {item = {kind}} -> ( match kind with diff --git a/tests/analysis_tests/tests/src/AppliedFunctor.res b/tests/analysis_tests/tests/src/AppliedFunctor.res index a442c375de..78d0beffcd 100644 --- a/tests/analysis_tests/tests/src/AppliedFunctor.res +++ b/tests/analysis_tests/tests/src/AppliedFunctor.res @@ -45,3 +45,20 @@ module N = WithNested(IntOrd) let n = N.Nested.value // ^ref + +module WithShapes = (O: Ord) => { + type point = {x: int} + type shape = Dot | Box(int) +} + +module Shapes = WithShapes(IntOrd) + +let p: Shapes.point = {x: 1} +let px = p.x +// ^def + +let shape = Shapes.Box(1) +// ^def + +let py = p.x +// ^ren y diff --git a/tests/analysis_tests/tests/src/expected/AppliedFunctor.res.txt b/tests/analysis_tests/tests/src/expected/AppliedFunctor.res.txt index 04023d5638..7a9e9026a3 100644 --- a/tests/analysis_tests/tests/src/expected/AppliedFunctor.res.txt +++ b/tests/analysis_tests/tests/src/expected/AppliedFunctor.res.txt @@ -168,3 +168,58 @@ References src/AppliedFunctor.res 45:10 } ] +Definition src/AppliedFunctor.res 56:11 +{ + "range": { + "end": { "character": 17, "line": 49 }, + "start": { "character": 16, "line": 49 } + }, + "uri": "file:///AppliedFunctor.res" +} + +Definition src/AppliedFunctor.res 59:19 +{ + "range": { + "end": { "character": 24, "line": 50 }, + "start": { "character": 21, "line": 50 } + }, + "uri": "file:///AppliedFunctor.res" +} + +Rename src/AppliedFunctor.res 62:11 y +[ + { + "edits": [ + { + "newText": "y", + "range": { + "end": { "character": 17, "line": 49 }, + "start": { "character": 16, "line": 49 } + } + }, + { + "newText": "y", + "range": { + "end": { "character": 24, "line": 55 }, + "start": { "character": 23, "line": 55 } + } + }, + { + "newText": "y", + "range": { + "end": { "character": 12, "line": 56 }, + "start": { "character": 11, "line": 56 } + } + }, + { + "newText": "y", + "range": { + "end": { "character": 12, "line": 62 }, + "start": { "character": 11, "line": 62 } + } + } + ], + "textDocument": { "uri": "file:///AppliedFunctor.res", "version": 0 } + } +] + From 9ae3cc852b6983a50aeb1ca87cdccab03e41c546 Mon Sep 17 00:00:00 2001 From: Christoph Knittel Date: Thu, 29 Oct 2026 06:11:22 +0000 Subject: [PATCH 07/12] Register module types of an applied functor's signature Codex review on #9084: the applied signature's module types were dropped, because the signature walk handled modules but not module types, so a member declared with one, such as `Applied.Nested.value` with `module Nested: S`, no longer resolved. Module types are registered like those of a structure now. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Kbf3gLMawVgtmiKy3TMBc3 Claude-Session: https://claude.ai/code/session_01Kr4cjoUi2f5VpMvE6wT9bo Signed-off-by: Christoph Knittel --- analysis/src/process_cmt.ml | 22 +++++++++++++++++++ .../tests/src/AppliedFunctor.res | 17 ++++++++++++++ .../tests/src/expected/AppliedFunctor.res.txt | 13 +++++++++++ 3 files changed, 52 insertions(+) diff --git a/analysis/src/process_cmt.ml b/analysis/src/process_cmt.ml index 1a7763ca94..eb1b98c0b7 100644 --- a/analysis/src/process_cmt.ml +++ b/analysis/src/process_cmt.ml @@ -165,6 +165,28 @@ let rec for_type_signature_item ~(env : Shared_types.Env.t) loc = declared.extent_loc; }; ] + | Sig_modtype (ident, {mtd_type = Some mod_type; mtd_attributes; mtd_loc}) -> + let name = Ident.name ident in + let env = env |> Env.add_module_type ~name in + let declared = + add_declared ~extent:mtd_loc + ~item:(for_type_module ~name ~env mod_type) + ~name:(Location.mkloc name mtd_loc) + ~stamp:(Ident.binding_time ident) ~env mtd_attributes + (Exported.add exported Exported.Module) + Stamps.add_module + in + [ + { + Module.kind = + Module + {type_ = declared.item; is_module_type = is_module_type declared}; + name = declared.name.txt; + docstring = declared.docstring; + deprecated = declared.deprecated; + loc = declared.extent_loc; + }; + ] | _ -> [] and for_type_signature ~name ~env signature = diff --git a/tests/analysis_tests/tests/src/AppliedFunctor.res b/tests/analysis_tests/tests/src/AppliedFunctor.res index 78d0beffcd..d0bb20a839 100644 --- a/tests/analysis_tests/tests/src/AppliedFunctor.res +++ b/tests/analysis_tests/tests/src/AppliedFunctor.res @@ -62,3 +62,20 @@ let shape = Shapes.Box(1) let py = p.x // ^ren y + +module WithModType = (O: Ord) => { + module type S = { + let value: int + } + module Nested: S = { + let value = 1 + } +} + +module Applied = WithModType(IntOrd) + +let nv = Applied.Nested.value +// ^hov + +// let nv2 = Applied.Nested. +// ^com diff --git a/tests/analysis_tests/tests/src/expected/AppliedFunctor.res.txt b/tests/analysis_tests/tests/src/expected/AppliedFunctor.res.txt index 7a9e9026a3..c64a1d0d7a 100644 --- a/tests/analysis_tests/tests/src/expected/AppliedFunctor.res.txt +++ b/tests/analysis_tests/tests/src/expected/AppliedFunctor.res.txt @@ -223,3 +223,16 @@ Rename src/AppliedFunctor.res 62:11 y } ] +Hover src/AppliedFunctor.res 76:24 +{ "contents": { "kind": "markdown", "value": "```rescript\nint\n```" } } + +Complete src/AppliedFunctor.res 79:28 +pos_cursor:[79:28] pos_no_white:[79:27] Found expr:[79:13->79:28] +Pexp_ident Applied.Nested.:[79:13->79:28] +Completable: Cpath Value[Applied, Nested, ""] +Package opens Stdlib.place holder Pervasives.JsxModules.place holder +Resolved opens 1 Stdlib +ContextPath Value[Applied, Nested, ""] +Path Applied.Nested. +[ { "detail": "int", "kind": 12, "label": "value", "tags": [] } ] + From 420145e1d25915ee0e8dd0a3a7e29b6a48c8fc2a Mon Sep 17 00:00:00 2001 From: Christoph Knittel Date: Thu, 29 Oct 2026 09:53:14 +0000 Subject: [PATCH 08/12] Map the members of an applied functor's module types to their declarations Codex review on #9084: only a module type's own identifier was mapped to the functor body's, not those inside its signature, so a member reached through the applied module type, such as `X.value` with `X: Applied.S`, was missing from the references and renames of `S.value`. Module type signatures are now walked like those of modules. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Kbf3gGkPma9LAZimuQjnrP Claude-Session: https://claude.ai/code/session_01Kr4cjoUi2f5VpMvE6wT9bo Signed-off-by: Christoph Knittel --- analysis/src/process_cmt.ml | 8 +- .../tests/src/AppliedFunctor.res | 66 +++++ .../tests/src/expected/AppliedFunctor.res.txt | 243 +++++++++++++++++- 3 files changed, 311 insertions(+), 6 deletions(-) diff --git a/analysis/src/process_cmt.ml b/analysis/src/process_cmt.ml index eb1b98c0b7..b24ac7e1a3 100644 --- a/analysis/src/process_cmt.ml +++ b/analysis/src/process_cmt.ml @@ -227,12 +227,16 @@ let rec record_renamed_stamps table (original : Types.module_type) match (item, renamed_item) with | Sig_value (id, _), Sig_value (id', _) | Sig_type (id, _, _), Sig_type (id', _, _) - | Sig_typext (id, _, _), Sig_typext (id', _, _) - | Sig_modtype (id, _), Sig_modtype (id', _) -> + | Sig_typext (id, _, _), Sig_typext (id', _, _) -> Some (id, id') | Sig_module (id, md, _), Sig_module (id', md', _) -> record_renamed_stamps table md.md_type md'.md_type; Some (id, id') + | Sig_modtype (id, mtd), Sig_modtype (id', mtd') -> + (match (mtd.mtd_type, mtd'.mtd_type) with + | Some mty, Some mty' -> record_renamed_stamps table mty mty' + | _ -> ()); + Some (id, id') | _ -> None in match ids with diff --git a/tests/analysis_tests/tests/src/AppliedFunctor.res b/tests/analysis_tests/tests/src/AppliedFunctor.res index d0bb20a839..d56b6d6dc0 100644 --- a/tests/analysis_tests/tests/src/AppliedFunctor.res +++ b/tests/analysis_tests/tests/src/AppliedFunctor.res @@ -66,6 +66,7 @@ let py = p.x module WithModType = (O: Ord) => { module type S = { let value: int + // ^ref } module Nested: S = { let value = 1 @@ -79,3 +80,68 @@ let nv = Applied.Nested.value // let nv2 = Applied.Nested. // ^com + +let nv3 = Applied.Nested.value +// ^ren renamed + +module WithDeepModType = (O: Ord) => { + module Outer = { + module type T = { + module Inner: { + let deep: int + // ^ref + } + module type Abstract + } + module First: T = { + module Inner = { + let deep = 1 + } + module type Abstract = {} + } + module Second: T = { + module Inner = { + let deep = 2 + } + module type Abstract = {} + } + } +} + +module Deep = WithDeepModType(IntOrd) + +let d1 = Deep.Outer.First.Inner.deep +let d2 = Deep.Outer.Second.Inner.deep +// ^ref + +module type WithSig = { + module type S = { + let sigValue: int + // ^ref + } + module Nested: S +} + +module WithSigResult = (O: Ord): WithSig => { + module type S = { + let sigValue: int + } + module Nested = { + let sigValue = 1 + } +} + +module AppliedSig = WithSigResult(IntOrd) + +let sv = AppliedSig.Nested.sigValue +// ^ref + +module UseAppliedModType = (X: Applied.S) => { + let v = X.value + // ^ref +} + +module UseDeepModType = (Y: Deep.Outer.T) => { + let d = Y.Inner.deep + // ^ref +} diff --git a/tests/analysis_tests/tests/src/expected/AppliedFunctor.res.txt b/tests/analysis_tests/tests/src/expected/AppliedFunctor.res.txt index c64a1d0d7a..cc3dbf6303 100644 --- a/tests/analysis_tests/tests/src/expected/AppliedFunctor.res.txt +++ b/tests/analysis_tests/tests/src/expected/AppliedFunctor.res.txt @@ -223,12 +223,44 @@ Rename src/AppliedFunctor.res 62:11 y } ] -Hover src/AppliedFunctor.res 76:24 +References src/AppliedFunctor.res 67:8 +[ + { + "range": { + "end": { "character": 18, "line": 67 }, + "start": { "character": 4, "line": 67 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 29, "line": 77 }, + "start": { "character": 24, "line": 77 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 30, "line": 83 }, + "start": { "character": 25, "line": 83 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 17, "line": 139 }, + "start": { "character": 12, "line": 139 } + }, + "uri": "file:///AppliedFunctor.res" + } +] + +Hover src/AppliedFunctor.res 77:24 { "contents": { "kind": "markdown", "value": "```rescript\nint\n```" } } -Complete src/AppliedFunctor.res 79:28 -pos_cursor:[79:28] pos_no_white:[79:27] Found expr:[79:13->79:28] -Pexp_ident Applied.Nested.:[79:13->79:28] +Complete src/AppliedFunctor.res 80:28 +pos_cursor:[80:28] pos_no_white:[80:27] Found expr:[80:13->80:28] +Pexp_ident Applied.Nested.:[80:13->80:28] Completable: Cpath Value[Applied, Nested, ""] Package opens Stdlib.place holder Pervasives.JsxModules.place holder Resolved opens 1 Stdlib @@ -236,3 +268,206 @@ ContextPath Value[Applied, Nested, ""] Path Applied.Nested. [ { "detail": "int", "kind": 12, "label": "value", "tags": [] } ] +Rename src/AppliedFunctor.res 83:25 renamed +[ + { + "edits": [ + { + "newText": "renamed", + "range": { + "end": { "character": 18, "line": 67 }, + "start": { "character": 4, "line": 67 } + } + }, + { + "newText": "renamed", + "range": { + "end": { "character": 29, "line": 77 }, + "start": { "character": 24, "line": 77 } + } + }, + { + "newText": "renamed", + "range": { + "end": { "character": 30, "line": 83 }, + "start": { "character": 25, "line": 83 } + } + }, + { + "newText": "renamed", + "range": { + "end": { "character": 17, "line": 139 }, + "start": { "character": 12, "line": 139 } + } + } + ], + "textDocument": { "uri": "file:///AppliedFunctor.res", "version": 0 } + } +] + +References src/AppliedFunctor.res 90:12 +[ + { + "range": { + "end": { "character": 21, "line": 90 }, + "start": { "character": 8, "line": 90 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 36, "line": 112 }, + "start": { "character": 32, "line": 112 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 37, "line": 113 }, + "start": { "character": 33, "line": 113 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 22, "line": 144 }, + "start": { "character": 18, "line": 144 } + }, + "uri": "file:///AppliedFunctor.res" + } +] + +References src/AppliedFunctor.res 113:33 +[ + { + "range": { + "end": { "character": 21, "line": 90 }, + "start": { "character": 8, "line": 90 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 36, "line": 112 }, + "start": { "character": 32, "line": 112 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 37, "line": 113 }, + "start": { "character": 33, "line": 113 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 22, "line": 144 }, + "start": { "character": 18, "line": 144 } + }, + "uri": "file:///AppliedFunctor.res" + } +] + +References src/AppliedFunctor.res 118:8 +get_loc_item #8: heuristic for JSX with at most one child +heuristic for: [makeProps, make, createElement], give the loc of `make` +[ + { + "range": { + "end": { "character": 21, "line": 118 }, + "start": { "character": 4, "line": 118 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 35, "line": 135 }, + "start": { "character": 27, "line": 135 } + }, + "uri": "file:///AppliedFunctor.res" + } +] + +References src/AppliedFunctor.res 135:27 +[ + { + "range": { + "end": { "character": 21, "line": 118 }, + "start": { "character": 4, "line": 118 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 35, "line": 135 }, + "start": { "character": 27, "line": 135 } + }, + "uri": "file:///AppliedFunctor.res" + } +] + +References src/AppliedFunctor.res 139:12 +[ + { + "range": { + "end": { "character": 18, "line": 67 }, + "start": { "character": 4, "line": 67 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 29, "line": 77 }, + "start": { "character": 24, "line": 77 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 30, "line": 83 }, + "start": { "character": 25, "line": 83 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 17, "line": 139 }, + "start": { "character": 12, "line": 139 } + }, + "uri": "file:///AppliedFunctor.res" + } +] + +References src/AppliedFunctor.res 144:18 +[ + { + "range": { + "end": { "character": 21, "line": 90 }, + "start": { "character": 8, "line": 90 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 36, "line": 112 }, + "start": { "character": 32, "line": 112 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 37, "line": 113 }, + "start": { "character": 33, "line": 113 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 22, "line": 144 }, + "start": { "character": 18, "line": 144 } + }, + "uri": "file:///AppliedFunctor.res" + } +] + From 7a3dd05b2b534a9f91a91df79851df5b03e2e394 Mon Sep 17 00:00:00 2001 From: Christoph Knittel Date: Thu, 29 Oct 2026 10:03:29 +0000 Subject: [PATCH 09/12] Register an inline functor's parameters before applying it Codex review on #9084: an immediately applied inline functor was no longer walked, so its parameters and declarations were missing and `O.compare` in its body had no definition. The functor is walked again before its application. The results of curried applications now take the body's identifiers from the functor applied first, as the compiler had already renamed those of the inner application. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Kbf3gGkPma9LAZimuQjnrP Claude-Session: https://claude.ai/code/session_01Kr4cjoUi2f5VpMvE6wT9bo Signed-off-by: Christoph Knittel --- analysis/src/process_cmt.ml | 30 +++-- .../tests/src/AppliedFunctorShapes.res | 64 ++++++++++ .../src/expected/AppliedFunctorShapes.res.txt | 114 ++++++++++++++++++ 3 files changed, 198 insertions(+), 10 deletions(-) create mode 100644 tests/analysis_tests/tests/src/AppliedFunctorShapes.res create mode 100644 tests/analysis_tests/tests/src/expected/AppliedFunctorShapes.res.txt diff --git a/analysis/src/process_cmt.ml b/analysis/src/process_cmt.ml index b24ac7e1a3..fd0ba11c18 100644 --- a/analysis/src/process_cmt.ml +++ b/analysis/src/process_cmt.ml @@ -248,6 +248,21 @@ let rec record_renamed_stamps table (original : Types.module_type) record_renamed_stamps table result renamed_result | _ -> () +(* The type of an applied functor with the identifiers of the body's + declarations. The functor's result signature has them; an application's + has them only when the argument is not a path, else the compiler already + renamed them, so the results of curried applications are taken from the + functor applied first. *) +let rec body_type (mod_expr : Typedtree.module_expr) = + match mod_expr.mod_desc with + | Tmod_apply (functor_, _arg, _coercion) -> ( + match body_type functor_ with + | Types.Mty_functor (_, _, result) -> result + | mty -> mty) + | Tmod_ident _ | Tmod_structure _ | Tmod_functor _ | Tmod_constraint _ + | Tmod_unpack _ -> + mod_expr.mod_type + let get_module_type_path mod_desc = match mod_desc with | Typedtree.Tmty_ident (path, _) | Tmty_alias (path, _) -> Some path @@ -734,7 +749,10 @@ and for_module ~env (mod_expr : Typedtree.module_expr) module_name = body, whose stamps are already taken, so they are renamed first, with stamps above any that a compilation hands out. Their locations are in the functor's body, so they are marked as coming from the - functor, like included items, for definitions to find its file. *) + functor, like included items, for definitions to find its file. + An inline functor's parameters and declarations are registered + first, for uses inside its body to resolve. *) + ignore (for_module ~env functor_ module_name); let env = env |> Env.add_module ~name:module_name in let env = match get_module_path functor_.mod_desc with @@ -745,15 +763,7 @@ and for_module ~env (mod_expr : Typedtree.module_expr) module_name = if Ident.current_time () < renamed_stamp_floor then Ident.set_current_time renamed_stamp_floor; let renamed = Subst.modtype Subst.identity mod_expr.mod_type in - (* The functor's result signature has the identifiers of the body's - declarations; the application's has them only when the argument is - not a path, else the compiler already renamed them. *) - let functor_result = - match functor_.mod_type with - | Mty_functor (_, _, result) -> result - | mty -> mty - in - record_renamed_stamps env.renamed_stamps functor_result renamed; + record_renamed_stamps env.renamed_stamps (body_type mod_expr) renamed; for_type_module ~name:module_name ~env renamed | Tmod_unpack (_expr, module_type) -> let env = env |> Env.add_module ~name:module_name in diff --git a/tests/analysis_tests/tests/src/AppliedFunctorShapes.res b/tests/analysis_tests/tests/src/AppliedFunctorShapes.res new file mode 100644 index 0000000000..e8bd28306c --- /dev/null +++ b/tests/analysis_tests/tests/src/AppliedFunctorShapes.res @@ -0,0 +1,64 @@ +module type Ord = { + type t + let compare: (t, t) => int +} + +module IntOrd = { + type t = int + let compare = (a: int, b) => a - b +} + +module Inline = ((O: Ord) => { + let cmp = O.compare + // ^def + let one = 1 +})(IntOrd) + +let one = Inline.one +// ^def + +module InlineCurried = ((A: Ord, B: Ord) => { + let both = (A.compare, B.compare) + // ^hov +})(IntOrd, IntOrd) + +let both = InlineCurried.both +// ^ref + +module Generative = (() => { + let fresh = 1 +})() + +let fresh = Generative.fresh +// ^def + +module MakeTwo = (A: Ord, B: Ord) => { + let pair = (A.compare, B.compare) +} + +module Two = MakeTwo(IntOrd, IntOrd) + +let pair = Two.pair +// ^ref + +module Constrained = ((O: Ord) => { + let c = O.compare + // ^def +}: (O: Ord) => { + let c: (O.t, O.t) => int +})(IntOrd) + +let c = Constrained.c +// ^def + +module ToIdent = (O: Ord) => IntOrd + +module Ident = ToIdent(IntOrd) + +let ic = Ident.compare +// ^def + +module Nest = MakeTwo(ToIdent(IntOrd), IntOrd) + +let np = Nest.pair +// ^def diff --git a/tests/analysis_tests/tests/src/expected/AppliedFunctorShapes.res.txt b/tests/analysis_tests/tests/src/expected/AppliedFunctorShapes.res.txt new file mode 100644 index 0000000000..0b773e67e6 --- /dev/null +++ b/tests/analysis_tests/tests/src/expected/AppliedFunctorShapes.res.txt @@ -0,0 +1,114 @@ +Definition src/AppliedFunctorShapes.res 11:14 +{ + "range": { + "end": { "character": 28, "line": 2 }, + "start": { "character": 2, "line": 2 } + }, + "uri": "file:///AppliedFunctorShapes.res" +} + +Definition src/AppliedFunctorShapes.res 16:17 +{ + "range": { + "end": { "character": 9, "line": 13 }, + "start": { "character": 6, "line": 13 } + }, + "uri": "file:///AppliedFunctorShapes.res" +} + +Hover src/AppliedFunctorShapes.res 20:27 +{ + "contents": { + "kind": "markdown", + "value": "```rescript\n(B.t, B.t) => int\n```\n\n---\n\n```\n \n```\n```rescript\ntype B.t\n```\nGo to: [Type definition](AppliedFunctorShapes.res#L2,3)\n" + } +} + +References src/AppliedFunctorShapes.res 24:25 +[ + { + "range": { + "end": { "character": 10, "line": 20 }, + "start": { "character": 6, "line": 20 } + }, + "uri": "file:///AppliedFunctorShapes.res" + }, + { + "range": { + "end": { "character": 29, "line": 24 }, + "start": { "character": 25, "line": 24 } + }, + "uri": "file:///AppliedFunctorShapes.res" + } +] + +Definition src/AppliedFunctorShapes.res 31:23 +{ + "range": { + "end": { "character": 11, "line": 28 }, + "start": { "character": 6, "line": 28 } + }, + "uri": "file:///AppliedFunctorShapes.res" +} + +References src/AppliedFunctorShapes.res 40:15 +[ + { + "range": { + "end": { "character": 10, "line": 35 }, + "start": { "character": 6, "line": 35 } + }, + "uri": "file:///AppliedFunctorShapes.res" + }, + { + "range": { + "end": { "character": 19, "line": 40 }, + "start": { "character": 15, "line": 40 } + }, + "uri": "file:///AppliedFunctorShapes.res" + }, + { + "range": { + "end": { "character": 18, "line": 62 }, + "start": { "character": 14, "line": 62 } + }, + "uri": "file:///AppliedFunctorShapes.res" + } +] + +Definition src/AppliedFunctorShapes.res 44:12 +{ + "range": { + "end": { "character": 28, "line": 2 }, + "start": { "character": 2, "line": 2 } + }, + "uri": "file:///AppliedFunctorShapes.res" +} + +Definition src/AppliedFunctorShapes.res 50:20 +{ + "range": { + "end": { "character": 26, "line": 47 }, + "start": { "character": 2, "line": 47 } + }, + "uri": "file:///AppliedFunctorShapes.res" +} + +Definition src/AppliedFunctorShapes.res 57:15 +{ + "range": { + "end": { "character": 13, "line": 7 }, + "start": { "character": 6, "line": 7 } + }, + "uri": "file:///AppliedFunctorShapes.res" +} + +Definition src/AppliedFunctorShapes.res 62:14 +{ + "range": { + "end": { "character": 10, "line": 35 }, + "start": { "character": 6, "line": 35 } + }, + "uri": "file:///AppliedFunctorShapes.res" +} + From e886bad25f9bbc6a6c2b919a3147e4b93ff67026 Mon Sep 17 00:00:00 2001 From: Christoph Knittel Date: Thu, 29 Oct 2026 10:32:13 +0000 Subject: [PATCH 10/12] Locate items of a functor applied from another file in that file Codex review on #9084: Go to Definition on a nested module and Go to Type Definition paired the functor file's locations with the caller's URI. Both now take the URI from the declaration's module path, like value definitions. Fields and constructors of such a functor are found in its file, and its items no longer register locations and references at the same positions of the caller's file. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Kbf3gGkPma9LAZimuQjnrP Claude-Session: https://claude.ai/code/session_01Kr4cjoUi2f5VpMvE6wT9bo Signed-off-by: Christoph Knittel --- analysis/src/process_extra.ml | 84 +++++---- analysis/src/references.ml | 94 +++++++--- .../tests/src/AppliedFunctor.res | 9 + .../tests/src/AppliedFunctorUse.res | 48 +++++ .../tests/src/expected/AppliedFunctor.res.txt | 55 ++++++ .../src/expected/AppliedFunctorUse.res.txt | 172 ++++++++++++++++++ 6 files changed, 398 insertions(+), 64 deletions(-) diff --git a/analysis/src/process_extra.ml b/analysis/src/process_extra.ml index 37c654ae96..c2285c19e0 100644 --- a/analysis/src/process_extra.ml +++ b/analysis/src/process_extra.ml @@ -12,52 +12,64 @@ let add_reference ~extra stamp loc = Hashtbl.find extra.internal_references stamp else [])) +(* Items read from another file's signature, such as those of a functor + applied from that file, have that file's locations, so they declare + nothing at those positions of this file. *) +let declared_in_file ~(file : File.t) (d : _ Declared.t) = + let source = d.extent_loc.loc_start.pos_fname in + d.extent_loc.loc_ghost || source = "" + || Filename.basename source = Filename.basename (Lsp.Uri.to_path file.uri) + let extra_for_file ~(file : File.t) = let extra = init_extra () in file.stamps |> Stamps.iter_modules (fun stamp (d : Module.t Declared.t) -> - add_loc_item extra d.name.loc (LModule (Definition (stamp, Module))); - add_reference ~extra stamp d.name.loc); + if declared_in_file ~file d then ( + add_loc_item extra d.name.loc (LModule (Definition (stamp, Module))); + add_reference ~extra stamp d.name.loc)); file.stamps |> Stamps.iter_values (fun stamp (d : Types.type_expr Declared.t) -> - add_loc_item extra d.name.loc - (Typed (d.name.txt, d.item, Definition (stamp, Value))); - add_reference ~extra stamp d.name.loc); + if declared_in_file ~file d then ( + add_loc_item extra d.name.loc + (Typed (d.name.txt, d.item, Definition (stamp, Value))); + add_reference ~extra stamp d.name.loc)); file.stamps |> Stamps.iter_types (fun stamp (d : Type.t Declared.t) -> - add_loc_item extra d.name.loc - (TypeDefinition (d.name.txt, d.item.Type.decl, stamp)); - add_reference ~extra stamp d.name.loc; - match d.item.Type.kind with - (* Fields and constructors of a type read from a signature, such as + if declared_in_file ~file d then ( + add_loc_item extra d.name.loc + (TypeDefinition (d.name.txt, d.item.Type.decl, stamp)); + add_reference ~extra stamp d.name.loc; + match d.item.Type.kind with + (* Fields and constructors of a type read from a signature, such as an applied functor's, have no location of their own; references to them are those of the declarations they come from. *) - | Record labels -> - labels - |> List.filter (fun {fname} -> not fname.loc.loc_ghost) - |> List.iter (fun {stamp; fname; typ} -> - add_reference ~extra stamp fname.loc; - add_loc_item extra fname.loc - (Typed (d.name.txt, typ, Definition (d.stamp, Field fname.txt)))) - | Variant constructors -> - constructors - |> List.filter (fun {Constructor.cname} -> not cname.loc.loc_ghost) - |> List.iter (fun {Constructor.stamp; cname} -> - add_reference ~extra stamp cname.loc; - let t = - { - Types.id = 0; - level = 0; - desc = - Tconstr - ( Path.Pident {Ident.stamp; name = d.name.txt; flags = 0}, - [], - ref Types.Mnil ); - } - in - add_loc_item extra cname.loc - (Typed (d.name.txt, t, Definition (d.stamp, Constructor cname.txt)))) - | _ -> ()); + | Record labels -> + labels + |> List.filter (fun {fname} -> not fname.loc.loc_ghost) + |> List.iter (fun {stamp; fname; typ} -> + add_reference ~extra stamp fname.loc; + add_loc_item extra fname.loc + (Typed (d.name.txt, typ, Definition (d.stamp, Field fname.txt)))) + | Variant constructors -> + constructors + |> List.filter (fun {Constructor.cname} -> not cname.loc.loc_ghost) + |> List.iter (fun {Constructor.stamp; cname} -> + add_reference ~extra stamp cname.loc; + let t = + { + Types.id = 0; + level = 0; + desc = + Tconstr + ( Path.Pident {Ident.stamp; name = d.name.txt; flags = 0}, + [], + ref Types.Mnil ); + } + in + add_loc_item extra cname.loc + (Typed + (d.name.txt, t, Definition (d.stamp, Constructor cname.txt)))) + | _ -> ())); extra let add_external_reference ~extra module_name path tip loc = diff --git a/analysis/src/references.ml b/analysis/src/references.ml index a6c94e4a1c..1b2886eb86 100644 --- a/analysis/src/references.ml +++ b/analysis/src/references.ml @@ -131,27 +131,61 @@ let original_type_stamp (file : File.t) stamp = | Some original -> original | None -> stamp +let field_of_type ({item = {kind}} : Type.t Declared.t) name = + match kind with + | Record fields -> fields |> List.find_opt (fun f -> f.fname.txt = name) + | _ -> None + +let constructor_of_type ({item = {kind}} : Type.t Declared.t) name = + match kind with + | Variant constructors -> + constructors + |> List.find_opt (fun const -> const.Constructor.cname.txt = name) + | _ -> None + let get_field (file : File.t) stamp name = match Stamps.find_type file.stamps (original_type_stamp file stamp) with | None -> None - | Some {item = {kind}} -> ( - match kind with - | Record fields -> fields |> List.find_opt (fun f -> f.fname.txt = name) - | _ -> None) + | Some declared -> field_of_type declared name let get_constructor (file : File.t) stamp name = match Stamps.find_type file.stamps (original_type_stamp file stamp) with | None -> None - | Some {item = {kind}} -> ( - match kind with - | Variant constructors -> ( + | Some declared -> constructor_of_type declared name + +(* The file a declaration is in. Items read from a signature, such as those + of an applied functor, are declared in this file but come from the + functor's file, which their module path records. *) +let declared_uri ~state ~package (file : File.t) (declared : _ Declared.t) = + Resolve_path.get_source_uri ~state ~env:(Query_env.from_file file) ~package + declared.module_path + +(* A type with its declaring file, for the locations of its fields and + constructors. A type of a functor applied from another file was renamed + from one that no file declares, and is looked up in the functor's file by + its location, among the types declared there rather than read from a + signature. *) +let source_type ~state ~package (file : File.t) stamp = + match Stamps.find_type file.stamps (original_type_stamp file stamp) with + | Some declared -> Some (file, declared) + | None -> ( + match Stamps.find_type file.stamps stamp with + | None -> None + | Some declared -> ( match - constructors - |> List.find_opt (fun const -> const.Constructor.cname.txt = name) + Cmt.full_from_uri ~state + ~uri:(declared_uri ~state ~package file declared) with | None -> None - | Some const -> Some const) - | _ -> None) + | Some {file = source} -> + Stamps.get_entries source.stamps + |> List.find_map (fun (source_stamp, kind) -> + match kind with + | Stamps.KType source_declared + when source_stamp < Process_cmt.renamed_stamp_floor + && source_declared.extent_loc = declared.extent_loc -> + Some (source, source_declared) + | _ -> None))) let exported_for_tip ~state ~env ~path ~package ~(tip : Tip.t) = match Resolve_path.resolve_path ~state ~env ~path ~package with @@ -310,24 +344,29 @@ let resolve_module_definition ~state ~(file : File.t) ~package stamp = | Some md -> ( match resolve_module_reference ~state ~file ~package md with | None -> None - | Some (file, declared) -> - let loc = - match declared with - | None -> Uri.to_top_level_loc file.uri - | Some declared -> validate_loc declared.name.loc declared.extent_loc - in - Some (file.uri, loc)) + | Some (file, declared) -> ( + match declared with + | None -> Some (file.uri, Uri.to_top_level_loc file.uri) + | Some declared -> + Some + ( declared_uri ~state ~package file declared, + validate_loc declared.name.loc declared.extent_loc ))) let definition ~state ~file ~package stamp (tip : Tip.t) = match tip with | Constructor name -> ( - match get_constructor file stamp name with + match source_type ~state ~package file stamp with | None -> None - | Some constructor -> Some (file.uri, constructor.cname.loc)) + | Some (file, declared) -> + constructor_of_type declared name + |> Option.map (fun (constructor : Constructor.t) -> + (file.uri, constructor.cname.loc))) | Field name -> ( - match get_field file stamp name with + match source_type ~state ~package file stamp with | None -> None - | Some field -> Some (file.uri, field.fname.loc)) + | Some (file, declared) -> + field_of_type declared name + |> Option.map (fun field -> (file.uri, field.fname.loc))) | Module -> resolve_module_definition ~state ~file ~package stamp | _ -> ( match declared_for_tip ~stamps:file.stamps stamp tip with @@ -341,11 +380,7 @@ let definition ~state ~file ~package stamp (tip : Tip.t) = | _ -> (file, declared) in let loc = validate_loc declared_impl.name.loc declared_impl.extent_loc in - let env = Query_env.from_file file_impl in - let uri = - Resolve_path.get_source_uri ~state ~env ~package - declared_impl.module_path - in + let uri = declared_uri ~state ~package file_impl declared_impl in maybe_log ("Inner uri " ^ Uri.to_string uri); Some (uri, loc)) @@ -425,7 +460,10 @@ let type_definition_for_loc_item ~state ~full:{file; package} loc_item = | None -> None | Some path -> ( match dig_constructor ~state ~env ~package path with - | Some (env, declared) -> Some (env.file.uri, declared.item.decl.type_loc) + | Some (env, declared) -> + Some + ( declared_uri ~state ~package env.file declared, + declared.item.decl.type_loc ) | None -> None)) let is_visible (declared : _ Declared.t) = diff --git a/tests/analysis_tests/tests/src/AppliedFunctor.res b/tests/analysis_tests/tests/src/AppliedFunctor.res index d56b6d6dc0..682673549e 100644 --- a/tests/analysis_tests/tests/src/AppliedFunctor.res +++ b/tests/analysis_tests/tests/src/AppliedFunctor.res @@ -145,3 +145,12 @@ module UseDeepModType = (Y: Deep.Outer.T) => { let d = Y.Inner.deep // ^ref } + +let nm = N.Nested.value +// ^def + +let sp: Shapes.point = {x: 2} +// ^typ + +let spx = sp.x +// ^def diff --git a/tests/analysis_tests/tests/src/AppliedFunctorUse.res b/tests/analysis_tests/tests/src/AppliedFunctorUse.res index c85a2efddf..73305b327a 100644 --- a/tests/analysis_tests/tests/src/AppliedFunctorUse.res +++ b/tests/analysis_tests/tests/src/AppliedFunctorUse.res @@ -11,3 +11,51 @@ let t: StrSet.t = [] // let _ = StrSet.add( // ^she + +let value: StrSet.t = [] +// ^typ + +let s2 = StrSet.add([], "b") +// ^ref + +module N = AppliedFunctor.WithNested(AppliedFunctor.IntOrd) + +let nv = N.Nested.value +// ^def + +let nv2 = N.Nested.value +// ^def + +let nv3 = N.Nested.value +// ^ref + +module Shapes = AppliedFunctor.WithShapes(AppliedFunctor.IntOrd) + +let p: Shapes.point = {x: 1} +// ^typ + +let px = p.x +// ^def + +let box = Shapes.Box(1) +// ^def + +module Local = (O: AppliedFunctor.Ord) => { + module Inner = { + type t = O.t + } +} + +module LocalApplied = Local(AppliedFunctor.IntOrd) + +let li: LocalApplied.Inner.t = 1 +// ^typ + +let li2: LocalApplied.Inner.t = 2 +// ^def + +let nv4 = N.Nested.value +// ^ref + +let p2: Shapes.point = {x: 2} +// ^ref diff --git a/tests/analysis_tests/tests/src/expected/AppliedFunctor.res.txt b/tests/analysis_tests/tests/src/expected/AppliedFunctor.res.txt index cc3dbf6303..8e6c64d281 100644 --- a/tests/analysis_tests/tests/src/expected/AppliedFunctor.res.txt +++ b/tests/analysis_tests/tests/src/expected/AppliedFunctor.res.txt @@ -147,6 +147,13 @@ References src/AppliedFunctor.res 32:11 "start": { "character": 10, "line": 45 } }, "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 17, "line": 148 }, + "start": { "character": 11, "line": 148 } + }, + "uri": "file:///AppliedFunctor.res" } ] @@ -165,6 +172,13 @@ References src/AppliedFunctor.res 45:10 "start": { "character": 10, "line": 45 } }, "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 17, "line": 148 }, + "start": { "character": 11, "line": 148 } + }, + "uri": "file:///AppliedFunctor.res" } ] @@ -217,6 +231,20 @@ Rename src/AppliedFunctor.res 62:11 y "end": { "character": 12, "line": 62 }, "start": { "character": 11, "line": 62 } } + }, + { + "newText": "y", + "range": { + "end": { "character": 25, "line": 151 }, + "start": { "character": 24, "line": 151 } + } + }, + { + "newText": "y", + "range": { + "end": { "character": 14, "line": 154 }, + "start": { "character": 13, "line": 154 } + } } ], "textDocument": { "uri": "file:///AppliedFunctor.res", "version": 0 } @@ -471,3 +499,30 @@ References src/AppliedFunctor.res 144:18 } ] +Definition src/AppliedFunctor.res 148:11 +{ + "range": { + "end": { "character": 3, "line": 35 }, + "start": { "character": 9, "line": 32 } + }, + "uri": "file:///AppliedFunctor.res" +} + +TypeDefinition src/AppliedFunctor.res 151:4 +{ + "range": { + "end": { "character": 23, "line": 49 }, + "start": { "character": 2, "line": 49 } + }, + "uri": "file:///AppliedFunctor.res" +} + +Definition src/AppliedFunctor.res 154:13 +{ + "range": { + "end": { "character": 17, "line": 49 }, + "start": { "character": 16, "line": 49 } + }, + "uri": "file:///AppliedFunctor.res" +} + diff --git a/tests/analysis_tests/tests/src/expected/AppliedFunctorUse.res.txt b/tests/analysis_tests/tests/src/expected/AppliedFunctorUse.res.txt index 459216d64a..f4a94e0f3d 100644 --- a/tests/analysis_tests/tests/src/expected/AppliedFunctorUse.res.txt +++ b/tests/analysis_tests/tests/src/expected/AppliedFunctorUse.res.txt @@ -52,3 +52,175 @@ extracted params: ] } +TypeDefinition src/AppliedFunctorUse.res 14:5 +{ + "range": { + "end": { "character": 21, "line": 6 }, + "start": { "character": 2, "line": 6 } + }, + "uri": "file:///AppliedFunctor.res" +} + +References src/AppliedFunctorUse.res 17:16 +[ + { + "range": { + "end": { "character": 18, "line": 5 }, + "start": { "character": 15, "line": 5 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 19, "line": 17 }, + "start": { "character": 16, "line": 17 } + }, + "uri": "file:///AppliedFunctorUse.res" + } +] + +Definition src/AppliedFunctorUse.res 22:11 +{ + "range": { + "end": { "character": 3, "line": 35 }, + "start": { "character": 9, "line": 32 } + }, + "uri": "file:///AppliedFunctor.res" +} + +Definition src/AppliedFunctorUse.res 25:19 +{ + "range": { + "end": { "character": 13, "line": 34 }, + "start": { "character": 8, "line": 34 } + }, + "uri": "file:///AppliedFunctor.res" +} + +References src/AppliedFunctorUse.res 28:19 +[ + { + "range": { + "end": { "character": 23, "line": 22 }, + "start": { "character": 18, "line": 22 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 24, "line": 25 }, + "start": { "character": 19, "line": 25 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 24, "line": 28 }, + "start": { "character": 19, "line": 28 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 24, "line": 56 }, + "start": { "character": 19, "line": 56 } + }, + "uri": "file:///AppliedFunctorUse.res" + } +] + +TypeDefinition src/AppliedFunctorUse.res 33:4 +{ + "range": { + "end": { "character": 23, "line": 49 }, + "start": { "character": 2, "line": 49 } + }, + "uri": "file:///AppliedFunctor.res" +} + +Definition src/AppliedFunctorUse.res 36:11 +{ + "range": { + "end": { "character": 17, "line": 49 }, + "start": { "character": 16, "line": 49 } + }, + "uri": "file:///AppliedFunctor.res" +} + +Definition src/AppliedFunctorUse.res 39:17 +{ + "range": { + "end": { "character": 24, "line": 50 }, + "start": { "character": 21, "line": 50 } + }, + "uri": "file:///AppliedFunctor.res" +} + +TypeDefinition src/AppliedFunctorUse.res 50:4 +{ + "range": { + "end": { "character": 16, "line": 44 }, + "start": { "character": 4, "line": 44 } + }, + "uri": "file:///AppliedFunctorUse.res" +} + +Definition src/AppliedFunctorUse.res 53:21 +{ + "range": { + "end": { "character": 19, "line": 48 }, + "start": { "character": 7, "line": 48 } + }, + "uri": "file:///AppliedFunctorUse.res" +} + +References src/AppliedFunctorUse.res 56:12 +[ + { + "range": { + "end": { "character": 17, "line": 22 }, + "start": { "character": 11, "line": 22 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 18, "line": 25 }, + "start": { "character": 12, "line": 25 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 18, "line": 28 }, + "start": { "character": 12, "line": 28 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 18, "line": 56 }, + "start": { "character": 12, "line": 56 } + }, + "uri": "file:///AppliedFunctorUse.res" + } +] + +References src/AppliedFunctorUse.res 59:15 +[ + { + "range": { + "end": { "character": 19, "line": 33 }, + "start": { "character": 14, "line": 33 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 20, "line": 59 }, + "start": { "character": 15, "line": 59 } + }, + "uri": "file:///AppliedFunctorUse.res" + } +] + From d22c654965df3847cb06c045207836f21491f07c Mon Sep 17 00:00:00 2001 From: Christoph Knittel Date: Thu, 29 Oct 2026 10:39:04 +0000 Subject: [PATCH 11/12] Refer uses of a functor applied from another file to that file's members A use of a member of a functor applied from another file referred to the caller's copy, so references and renames no longer crossed files as they did when the application resolved to the functor. Such a use refers to the functor's member again, for values, types, fields, constructors and modules, and a signature's nested modules now name their items' paths. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Kbf3gGkPma9LAZimuQjnrP Claude-Session: https://claude.ai/code/session_01Kr4cjoUi2f5VpMvE6wT9bo Signed-off-by: Christoph Knittel --- analysis/src/process_cmt.ml | 2 +- analysis/src/process_extra.ml | 87 ++- .../tests/src/AppliedFunctor.res | 2 + .../tests/src/AppliedFunctorUse.res | 15 + .../tests/src/expected/AppliedFunctor.res.txt | 502 ++++++++++++++---- .../src/expected/AppliedFunctorUse.res.txt | 445 +++++++++++++++- 6 files changed, 912 insertions(+), 141 deletions(-) diff --git a/analysis/src/process_cmt.ml b/analysis/src/process_cmt.ml index fd0ba11c18..8e613ce3d2 100644 --- a/analysis/src/process_cmt.ml +++ b/analysis/src/process_cmt.ml @@ -148,7 +148,7 @@ let rec for_type_signature_item ~(env : Shared_types.Env.t) let name = Ident.name ident in let declared = add_declared ~extent:md_loc - ~item:(for_type_module ~name ~env md_type) + ~item:(for_type_module ~name ~env:(env |> Env.add_module ~name) md_type) ~name:(Location.mkloc name md_loc) ~stamp:(Ident.binding_time ident) ~env md_attributes (Exported.add exported Exported.Module) diff --git a/analysis/src/process_extra.ml b/analysis/src/process_extra.ml index c2285c19e0..06acf01919 100644 --- a/analysis/src/process_extra.ml +++ b/analysis/src/process_extra.ml @@ -152,22 +152,55 @@ let extra_for_cmt ~(iterator : Tast_iterator.iterator) extra_for_signature_items ~iterator items; extra_for_parts parts -(* A use of an applied functor's member is also a use of the declaration - in the functor's body that it was renamed from. *) -let add_reference_with_original ~(env : Query_env.t) ~extra stamp loc = - add_reference ~extra stamp loc; - match Hashtbl.find_opt env.file.renamed_stamps stamp with - | Some original -> add_reference ~extra original loc - | None -> () +(* The module and path of a member of a functor applied from another file: + the functor's path followed by the member's path inside its result. *) +let applied_global ~(env : Query_env.t) stamp (tip : Tip.t) = + let declared = + let of_declared (d : _ Declared.t) = (d.module_path, d.name.txt) in + match tip with + | Value -> Stamps.find_value env.file.stamps stamp |> Option.map of_declared + | Type | Field _ | Constructor _ -> + Stamps.find_type env.file.stamps stamp |> Option.map of_declared + | Module -> + Stamps.find_module env.file.stamps stamp |> Option.map of_declared + in + let rec loop (module_path : Module_path.t) inner = + match module_path with + | IncludedModule (functor_path, _) -> ( + match Resolve_path.from_compiler_path ~env functor_path with + | Global (module_name, path) -> Some (module_name, path @ inner) + | Stamp _ | Exported _ | GlobalMod _ | NotFound -> None) + | ExportedModule {name; module_path} -> loop module_path (name :: inner) + | File _ | NotVisible -> None + in + match declared with + | Some (module_path, name) when stamp >= Process_cmt.renamed_stamp_floor -> + loop module_path [name] + | _ -> None + +(* A use of a local declaration. A use of an applied functor's member is + also a use of the declaration in the functor's body that it was renamed + from, and when the functor is in another file, it is a use of that file's + declaration, as uses of the functor's own members are, for references + across files to find it. *) +let reference_to_stamp ~(env : Query_env.t) ~extra stamp tip loc = + match applied_global ~env stamp tip with + | Some (module_name, path) -> + add_external_reference ~extra module_name path tip loc; + GlobalReference (module_name, path, tip) + | None -> + add_reference ~extra stamp loc; + (match Hashtbl.find_opt env.file.renamed_stamps stamp with + | Some original -> add_reference ~extra original loc + | None -> ()); + LocalReference (stamp, tip) let add_for_path ~env ~extra path lident loc typ tip = let ident_name = Longident.last lident in let ident_loc = Utils.end_of_location loc (String.length ident_name) in let loc_type = match Resolve_path.from_compiler_path ~env path with - | Stamp stamp -> - add_reference_with_original ~env ~extra stamp ident_loc; - LocalReference (stamp, tip) + | Stamp stamp -> reference_to_stamp ~env ~extra stamp tip ident_loc | NotFound -> NotFound | Global (module_name, path) -> add_external_reference ~extra module_name path tip ident_loc; @@ -178,9 +211,7 @@ let add_for_path ~env ~extra path lident loc typ tip = | Type -> Exported.find env.exported Exported.Type name | _ -> Exported.find env.exported Exported.Value name with - | Some stamp -> - add_reference_with_original ~env ~extra stamp ident_loc; - LocalReference (stamp, tip) + | Some stamp -> reference_to_stamp ~env ~extra stamp tip ident_loc | None -> NotFound) | GlobalMod _ -> NotFound in @@ -192,22 +223,28 @@ let add_for_path_parent ~env ~extra path loc = | GlobalMod module_name -> add_file_reference ~extra module_name loc; TopLevelModule module_name - | Stamp stamp -> - add_reference_with_original ~env ~extra stamp loc; - LModule (LocalReference (stamp, Module)) + | Stamp stamp -> LModule (reference_to_stamp ~env ~extra stamp Module loc) | NotFound -> LModule NotFound | Global (module_name, path) -> add_external_reference ~extra module_name path Module loc; LModule (GlobalReference (module_name, path, Module)) | Exported (env, name) -> ( match Exported.find env.exported Exported.Module name with - | Some stamp -> - add_reference_with_original ~env ~extra stamp loc; - LModule (LocalReference (stamp, Module)) + | Some stamp -> LModule (reference_to_stamp ~env ~extra stamp Module loc) | None -> LModule NotFound) in add_loc_item extra loc loc_type +(* A type of a functor applied from another file is that file's type, as + for its other members. *) +let get_type_at_stamp ~env stamp = + match applied_global ~env stamp Type with + | Some (module_name, path) -> `Global (module_name, path) + | None -> ( + match Stamps.find_type env.file.stamps stamp with + | Some declared_type -> `Local declared_type + | None -> `Not_found) + let get_type_at_path ~env path = match Resolve_path.from_compiler_path ~env path with | GlobalMod _ -> `Not_found @@ -216,16 +253,8 @@ let get_type_at_path ~env path = | Exported (env, name) -> ( match Exported.find env.exported Exported.Type name with | None -> `Not_found - | Some stamp -> ( - let declared_type = Stamps.find_type env.file.stamps stamp in - match declared_type with - | Some declared_type -> `Local declared_type - | None -> `Not_found)) - | Stamp stamp -> ( - let declared_type = Stamps.find_type env.file.stamps stamp in - match declared_type with - | Some declared_type -> `Local declared_type - | None -> `Not_found) + | Some stamp -> get_type_at_stamp ~env stamp) + | Stamp stamp -> get_type_at_stamp ~env stamp let add_for_field ~env ~extra ~record_type ~field_type {Asttypes.txt; loc} = match (Shared.dig record_type).desc with diff --git a/tests/analysis_tests/tests/src/AppliedFunctor.res b/tests/analysis_tests/tests/src/AppliedFunctor.res index 682673549e..cccf4d99a5 100644 --- a/tests/analysis_tests/tests/src/AppliedFunctor.res +++ b/tests/analysis_tests/tests/src/AppliedFunctor.res @@ -33,6 +33,7 @@ module WithNested = (O: Ord) => { module Nested = { // ^ref let value = 1 + // ^ren nestedValue } } @@ -48,6 +49,7 @@ let n = N.Nested.value module WithShapes = (O: Ord) => { type point = {x: int} + // ^ref type shape = Dot | Box(int) } diff --git a/tests/analysis_tests/tests/src/AppliedFunctorUse.res b/tests/analysis_tests/tests/src/AppliedFunctorUse.res index 73305b327a..edc5f79112 100644 --- a/tests/analysis_tests/tests/src/AppliedFunctorUse.res +++ b/tests/analysis_tests/tests/src/AppliedFunctorUse.res @@ -59,3 +59,18 @@ let nv4 = N.Nested.value let p2: Shapes.point = {x: 2} // ^ref + +let s3 = StrSet.add([], "c") +// ^ren addItem + +let p3: Shapes.point = {x: 3} +// ^ren location + +let nv5 = N.Nested.value +// ^ren renamedValue + +let px2 = p.x +// ^ref + +let box2 = Shapes.Box(2) +// ^ren Square diff --git a/tests/analysis_tests/tests/src/expected/AppliedFunctor.res.txt b/tests/analysis_tests/tests/src/expected/AppliedFunctor.res.txt index 8e6c64d281..7566834978 100644 --- a/tests/analysis_tests/tests/src/expected/AppliedFunctor.res.txt +++ b/tests/analysis_tests/tests/src/expected/AppliedFunctor.res.txt @@ -1,5 +1,26 @@ References src/AppliedFunctor.res 7:7 [ + { + "range": { + "end": { "character": 18, "line": 5 }, + "start": { "character": 15, "line": 5 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 19, "line": 17 }, + "start": { "character": 16, "line": 17 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 19, "line": 62 }, + "start": { "character": 16, "line": 62 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, { "range": { "end": { "character": 9, "line": 7 }, @@ -40,6 +61,27 @@ Hover src/AppliedFunctor.res 16:15 References src/AppliedFunctor.res 19:16 [ + { + "range": { + "end": { "character": 18, "line": 5 }, + "start": { "character": 15, "line": 5 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 19, "line": 17 }, + "start": { "character": 16, "line": 17 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 19, "line": 62 }, + "start": { "character": 16, "line": 62 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, { "range": { "end": { "character": 9, "line": 7 }, @@ -72,6 +114,32 @@ References src/AppliedFunctor.res 19:16 Rename src/AppliedFunctor.res 22:16 insert [ + { + "edits": [ + { + "newText": "insert", + "range": { + "end": { "character": 18, "line": 5 }, + "start": { "character": 15, "line": 5 } + } + }, + { + "newText": "insert", + "range": { + "end": { "character": 19, "line": 17 }, + "start": { "character": 16, "line": 17 } + } + }, + { + "newText": "insert", + "range": { + "end": { "character": 19, "line": 62 }, + "start": { "character": 16, "line": 62 } + } + } + ], + "textDocument": { "uri": "file:///AppliedFunctorUse.res", "version": 0 } + }, { "edits": [ { @@ -134,6 +202,41 @@ Path IntSet. References src/AppliedFunctor.res 32:11 [ + { + "range": { + "end": { "character": 17, "line": 22 }, + "start": { "character": 11, "line": 22 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 18, "line": 25 }, + "start": { "character": 12, "line": 25 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 18, "line": 28 }, + "start": { "character": 12, "line": 28 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 18, "line": 56 }, + "start": { "character": 12, "line": 56 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 18, "line": 68 }, + "start": { "character": 12, "line": 68 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, { "range": { "end": { "character": 15, "line": 32 }, @@ -143,22 +246,127 @@ References src/AppliedFunctor.res 32:11 }, { "range": { - "end": { "character": 16, "line": 45 }, - "start": { "character": 10, "line": 45 } + "end": { "character": 16, "line": 46 }, + "start": { "character": 10, "line": 46 } }, "uri": "file:///AppliedFunctor.res" }, { "range": { - "end": { "character": 17, "line": 148 }, - "start": { "character": 11, "line": 148 } + "end": { "character": 17, "line": 150 }, + "start": { "character": 11, "line": 150 } }, "uri": "file:///AppliedFunctor.res" } ] -References src/AppliedFunctor.res 45:10 +Rename src/AppliedFunctor.res 34:8 nestedValue +[ + { + "edits": [ + { + "newText": "nestedValue", + "range": { + "end": { "character": 23, "line": 22 }, + "start": { "character": 18, "line": 22 } + } + }, + { + "newText": "nestedValue", + "range": { + "end": { "character": 24, "line": 25 }, + "start": { "character": 19, "line": 25 } + } + }, + { + "newText": "nestedValue", + "range": { + "end": { "character": 24, "line": 28 }, + "start": { "character": 19, "line": 28 } + } + }, + { + "newText": "nestedValue", + "range": { + "end": { "character": 24, "line": 56 }, + "start": { "character": 19, "line": 56 } + } + }, + { + "newText": "nestedValue", + "range": { + "end": { "character": 24, "line": 68 }, + "start": { "character": 19, "line": 68 } + } + } + ], + "textDocument": { "uri": "file:///AppliedFunctorUse.res", "version": 0 } + }, + { + "edits": [ + { + "newText": "nestedValue", + "range": { + "end": { "character": 13, "line": 34 }, + "start": { "character": 8, "line": 34 } + } + }, + { + "newText": "nestedValue", + "range": { + "end": { "character": 22, "line": 46 }, + "start": { "character": 17, "line": 46 } + } + }, + { + "newText": "nestedValue", + "range": { + "end": { "character": 23, "line": 150 }, + "start": { "character": 18, "line": 150 } + } + } + ], + "textDocument": { "uri": "file:///AppliedFunctor.res", "version": 0 } + } +] + +References src/AppliedFunctor.res 46:10 [ + { + "range": { + "end": { "character": 17, "line": 22 }, + "start": { "character": 11, "line": 22 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 18, "line": 25 }, + "start": { "character": 12, "line": 25 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 18, "line": 28 }, + "start": { "character": 12, "line": 28 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 18, "line": 56 }, + "start": { "character": 12, "line": 56 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 18, "line": 68 }, + "start": { "character": 12, "line": 68 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, { "range": { "end": { "character": 15, "line": 32 }, @@ -168,82 +376,168 @@ References src/AppliedFunctor.res 45:10 }, { "range": { - "end": { "character": 16, "line": 45 }, - "start": { "character": 10, "line": 45 } + "end": { "character": 16, "line": 46 }, + "start": { "character": 10, "line": 46 } }, "uri": "file:///AppliedFunctor.res" }, { "range": { - "end": { "character": 17, "line": 148 }, - "start": { "character": 11, "line": 148 } + "end": { "character": 17, "line": 150 }, + "start": { "character": 11, "line": 150 } }, "uri": "file:///AppliedFunctor.res" } ] -Definition src/AppliedFunctor.res 56:11 +References src/AppliedFunctor.res 50:7 +[ + { + "range": { + "end": { "character": 19, "line": 33 }, + "start": { "character": 14, "line": 33 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 20, "line": 59 }, + "start": { "character": 15, "line": 59 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 20, "line": 65 }, + "start": { "character": 15, "line": 65 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 12, "line": 50 }, + "start": { "character": 7, "line": 50 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 19, "line": 57 }, + "start": { "character": 14, "line": 57 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 20, "line": 153 }, + "start": { "character": 15, "line": 153 } + }, + "uri": "file:///AppliedFunctor.res" + } +] + +Definition src/AppliedFunctor.res 58:11 { "range": { - "end": { "character": 17, "line": 49 }, - "start": { "character": 16, "line": 49 } + "end": { "character": 17, "line": 50 }, + "start": { "character": 16, "line": 50 } }, "uri": "file:///AppliedFunctor.res" } -Definition src/AppliedFunctor.res 59:19 +Definition src/AppliedFunctor.res 61:19 { "range": { - "end": { "character": 24, "line": 50 }, - "start": { "character": 21, "line": 50 } + "end": { "character": 24, "line": 52 }, + "start": { "character": 21, "line": 52 } }, "uri": "file:///AppliedFunctor.res" } -Rename src/AppliedFunctor.res 62:11 y +Rename src/AppliedFunctor.res 64:11 y [ { "edits": [ { "newText": "y", "range": { - "end": { "character": 17, "line": 49 }, - "start": { "character": 16, "line": 49 } + "end": { "character": 24, "line": 33 }, + "start": { "character": 23, "line": 33 } + } + }, + { + "newText": "y", + "range": { + "end": { "character": 12, "line": 36 }, + "start": { "character": 11, "line": 36 } + } + }, + { + "newText": "y", + "range": { + "end": { "character": 25, "line": 59 }, + "start": { "character": 24, "line": 59 } + } + }, + { + "newText": "y", + "range": { + "end": { "character": 25, "line": 65 }, + "start": { "character": 24, "line": 65 } + } + }, + { + "newText": "y", + "range": { + "end": { "character": 13, "line": 71 }, + "start": { "character": 12, "line": 71 } + } + } + ], + "textDocument": { "uri": "file:///AppliedFunctorUse.res", "version": 0 } + }, + { + "edits": [ + { + "newText": "y", + "range": { + "end": { "character": 17, "line": 50 }, + "start": { "character": 16, "line": 50 } } }, { "newText": "y", "range": { - "end": { "character": 24, "line": 55 }, - "start": { "character": 23, "line": 55 } + "end": { "character": 24, "line": 57 }, + "start": { "character": 23, "line": 57 } } }, { "newText": "y", "range": { - "end": { "character": 12, "line": 56 }, - "start": { "character": 11, "line": 56 } + "end": { "character": 12, "line": 58 }, + "start": { "character": 11, "line": 58 } } }, { "newText": "y", "range": { - "end": { "character": 12, "line": 62 }, - "start": { "character": 11, "line": 62 } + "end": { "character": 12, "line": 64 }, + "start": { "character": 11, "line": 64 } } }, { "newText": "y", "range": { - "end": { "character": 25, "line": 151 }, - "start": { "character": 24, "line": 151 } + "end": { "character": 25, "line": 153 }, + "start": { "character": 24, "line": 153 } } }, { "newText": "y", "range": { - "end": { "character": 14, "line": 154 }, - "start": { "character": 13, "line": 154 } + "end": { "character": 14, "line": 156 }, + "start": { "character": 13, "line": 156 } } } ], @@ -251,44 +545,44 @@ Rename src/AppliedFunctor.res 62:11 y } ] -References src/AppliedFunctor.res 67:8 +References src/AppliedFunctor.res 69:8 [ { "range": { - "end": { "character": 18, "line": 67 }, - "start": { "character": 4, "line": 67 } + "end": { "character": 18, "line": 69 }, + "start": { "character": 4, "line": 69 } }, "uri": "file:///AppliedFunctor.res" }, { "range": { - "end": { "character": 29, "line": 77 }, - "start": { "character": 24, "line": 77 } + "end": { "character": 29, "line": 79 }, + "start": { "character": 24, "line": 79 } }, "uri": "file:///AppliedFunctor.res" }, { "range": { - "end": { "character": 30, "line": 83 }, - "start": { "character": 25, "line": 83 } + "end": { "character": 30, "line": 85 }, + "start": { "character": 25, "line": 85 } }, "uri": "file:///AppliedFunctor.res" }, { "range": { - "end": { "character": 17, "line": 139 }, - "start": { "character": 12, "line": 139 } + "end": { "character": 17, "line": 141 }, + "start": { "character": 12, "line": 141 } }, "uri": "file:///AppliedFunctor.res" } ] -Hover src/AppliedFunctor.res 77:24 +Hover src/AppliedFunctor.res 79:24 { "contents": { "kind": "markdown", "value": "```rescript\nint\n```" } } -Complete src/AppliedFunctor.res 80:28 -pos_cursor:[80:28] pos_no_white:[80:27] Found expr:[80:13->80:28] -Pexp_ident Applied.Nested.:[80:13->80:28] +Complete src/AppliedFunctor.res 82:28 +pos_cursor:[82:28] pos_no_white:[82:27] Found expr:[82:13->82:28] +Pexp_ident Applied.Nested.:[82:13->82:28] Completable: Cpath Value[Applied, Nested, ""] Package opens Stdlib.place holder Pervasives.JsxModules.place holder Resolved opens 1 Stdlib @@ -296,36 +590,36 @@ ContextPath Value[Applied, Nested, ""] Path Applied.Nested. [ { "detail": "int", "kind": 12, "label": "value", "tags": [] } ] -Rename src/AppliedFunctor.res 83:25 renamed +Rename src/AppliedFunctor.res 85:25 renamed [ { "edits": [ { "newText": "renamed", "range": { - "end": { "character": 18, "line": 67 }, - "start": { "character": 4, "line": 67 } + "end": { "character": 18, "line": 69 }, + "start": { "character": 4, "line": 69 } } }, { "newText": "renamed", "range": { - "end": { "character": 29, "line": 77 }, - "start": { "character": 24, "line": 77 } + "end": { "character": 29, "line": 79 }, + "start": { "character": 24, "line": 79 } } }, { "newText": "renamed", "range": { - "end": { "character": 30, "line": 83 }, - "start": { "character": 25, "line": 83 } + "end": { "character": 30, "line": 85 }, + "start": { "character": 25, "line": 85 } } }, { "newText": "renamed", "range": { - "end": { "character": 17, "line": 139 }, - "start": { "character": 12, "line": 139 } + "end": { "character": 17, "line": 141 }, + "start": { "character": 12, "line": 141 } } } ], @@ -333,195 +627,195 @@ Rename src/AppliedFunctor.res 83:25 renamed } ] -References src/AppliedFunctor.res 90:12 +References src/AppliedFunctor.res 92:12 [ { "range": { - "end": { "character": 21, "line": 90 }, - "start": { "character": 8, "line": 90 } + "end": { "character": 21, "line": 92 }, + "start": { "character": 8, "line": 92 } }, "uri": "file:///AppliedFunctor.res" }, { "range": { - "end": { "character": 36, "line": 112 }, - "start": { "character": 32, "line": 112 } + "end": { "character": 36, "line": 114 }, + "start": { "character": 32, "line": 114 } }, "uri": "file:///AppliedFunctor.res" }, { "range": { - "end": { "character": 37, "line": 113 }, - "start": { "character": 33, "line": 113 } + "end": { "character": 37, "line": 115 }, + "start": { "character": 33, "line": 115 } }, "uri": "file:///AppliedFunctor.res" }, { "range": { - "end": { "character": 22, "line": 144 }, - "start": { "character": 18, "line": 144 } + "end": { "character": 22, "line": 146 }, + "start": { "character": 18, "line": 146 } }, "uri": "file:///AppliedFunctor.res" } ] -References src/AppliedFunctor.res 113:33 +References src/AppliedFunctor.res 115:33 [ { "range": { - "end": { "character": 21, "line": 90 }, - "start": { "character": 8, "line": 90 } + "end": { "character": 21, "line": 92 }, + "start": { "character": 8, "line": 92 } }, "uri": "file:///AppliedFunctor.res" }, { "range": { - "end": { "character": 36, "line": 112 }, - "start": { "character": 32, "line": 112 } + "end": { "character": 36, "line": 114 }, + "start": { "character": 32, "line": 114 } }, "uri": "file:///AppliedFunctor.res" }, { "range": { - "end": { "character": 37, "line": 113 }, - "start": { "character": 33, "line": 113 } + "end": { "character": 37, "line": 115 }, + "start": { "character": 33, "line": 115 } }, "uri": "file:///AppliedFunctor.res" }, { "range": { - "end": { "character": 22, "line": 144 }, - "start": { "character": 18, "line": 144 } + "end": { "character": 22, "line": 146 }, + "start": { "character": 18, "line": 146 } }, "uri": "file:///AppliedFunctor.res" } ] -References src/AppliedFunctor.res 118:8 +References src/AppliedFunctor.res 120:8 get_loc_item #8: heuristic for JSX with at most one child heuristic for: [makeProps, make, createElement], give the loc of `make` [ { "range": { - "end": { "character": 21, "line": 118 }, - "start": { "character": 4, "line": 118 } + "end": { "character": 21, "line": 120 }, + "start": { "character": 4, "line": 120 } }, "uri": "file:///AppliedFunctor.res" }, { "range": { - "end": { "character": 35, "line": 135 }, - "start": { "character": 27, "line": 135 } + "end": { "character": 35, "line": 137 }, + "start": { "character": 27, "line": 137 } }, "uri": "file:///AppliedFunctor.res" } ] -References src/AppliedFunctor.res 135:27 +References src/AppliedFunctor.res 137:27 [ { "range": { - "end": { "character": 21, "line": 118 }, - "start": { "character": 4, "line": 118 } + "end": { "character": 21, "line": 120 }, + "start": { "character": 4, "line": 120 } }, "uri": "file:///AppliedFunctor.res" }, { "range": { - "end": { "character": 35, "line": 135 }, - "start": { "character": 27, "line": 135 } + "end": { "character": 35, "line": 137 }, + "start": { "character": 27, "line": 137 } }, "uri": "file:///AppliedFunctor.res" } ] -References src/AppliedFunctor.res 139:12 +References src/AppliedFunctor.res 141:12 [ { "range": { - "end": { "character": 18, "line": 67 }, - "start": { "character": 4, "line": 67 } + "end": { "character": 18, "line": 69 }, + "start": { "character": 4, "line": 69 } }, "uri": "file:///AppliedFunctor.res" }, { "range": { - "end": { "character": 29, "line": 77 }, - "start": { "character": 24, "line": 77 } + "end": { "character": 29, "line": 79 }, + "start": { "character": 24, "line": 79 } }, "uri": "file:///AppliedFunctor.res" }, { "range": { - "end": { "character": 30, "line": 83 }, - "start": { "character": 25, "line": 83 } + "end": { "character": 30, "line": 85 }, + "start": { "character": 25, "line": 85 } }, "uri": "file:///AppliedFunctor.res" }, { "range": { - "end": { "character": 17, "line": 139 }, - "start": { "character": 12, "line": 139 } + "end": { "character": 17, "line": 141 }, + "start": { "character": 12, "line": 141 } }, "uri": "file:///AppliedFunctor.res" } ] -References src/AppliedFunctor.res 144:18 +References src/AppliedFunctor.res 146:18 [ { "range": { - "end": { "character": 21, "line": 90 }, - "start": { "character": 8, "line": 90 } + "end": { "character": 21, "line": 92 }, + "start": { "character": 8, "line": 92 } }, "uri": "file:///AppliedFunctor.res" }, { "range": { - "end": { "character": 36, "line": 112 }, - "start": { "character": 32, "line": 112 } + "end": { "character": 36, "line": 114 }, + "start": { "character": 32, "line": 114 } }, "uri": "file:///AppliedFunctor.res" }, { "range": { - "end": { "character": 37, "line": 113 }, - "start": { "character": 33, "line": 113 } + "end": { "character": 37, "line": 115 }, + "start": { "character": 33, "line": 115 } }, "uri": "file:///AppliedFunctor.res" }, { "range": { - "end": { "character": 22, "line": 144 }, - "start": { "character": 18, "line": 144 } + "end": { "character": 22, "line": 146 }, + "start": { "character": 18, "line": 146 } }, "uri": "file:///AppliedFunctor.res" } ] -Definition src/AppliedFunctor.res 148:11 +Definition src/AppliedFunctor.res 150:11 { "range": { - "end": { "character": 3, "line": 35 }, + "end": { "character": 3, "line": 36 }, "start": { "character": 9, "line": 32 } }, "uri": "file:///AppliedFunctor.res" } -TypeDefinition src/AppliedFunctor.res 151:4 +TypeDefinition src/AppliedFunctor.res 153:4 { "range": { - "end": { "character": 23, "line": 49 }, - "start": { "character": 2, "line": 49 } + "end": { "character": 23, "line": 50 }, + "start": { "character": 2, "line": 50 } }, "uri": "file:///AppliedFunctor.res" } -Definition src/AppliedFunctor.res 154:13 +Definition src/AppliedFunctor.res 156:13 { "range": { - "end": { "character": 17, "line": 49 }, - "start": { "character": 16, "line": 49 } + "end": { "character": 17, "line": 50 }, + "start": { "character": 16, "line": 50 } }, "uri": "file:///AppliedFunctor.res" } diff --git a/tests/analysis_tests/tests/src/expected/AppliedFunctorUse.res.txt b/tests/analysis_tests/tests/src/expected/AppliedFunctorUse.res.txt index f4a94e0f3d..662a6fac4f 100644 --- a/tests/analysis_tests/tests/src/expected/AppliedFunctorUse.res.txt +++ b/tests/analysis_tests/tests/src/expected/AppliedFunctorUse.res.txt @@ -76,13 +76,48 @@ References src/AppliedFunctorUse.res 17:16 "start": { "character": 16, "line": 17 } }, "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 19, "line": 62 }, + "start": { "character": 16, "line": 62 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 9, "line": 7 }, + "start": { "character": 6, "line": 7 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 18, "line": 16 }, + "start": { "character": 15, "line": 16 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 19, "line": 19 }, + "start": { "character": 16, "line": 19 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 19, "line": 22 }, + "start": { "character": 16, "line": 22 } + }, + "uri": "file:///AppliedFunctor.res" } ] Definition src/AppliedFunctorUse.res 22:11 { "range": { - "end": { "character": 3, "line": 35 }, + "end": { "character": 15, "line": 32 }, "start": { "character": 9, "line": 32 } }, "uri": "file:///AppliedFunctor.res" @@ -126,14 +161,42 @@ References src/AppliedFunctorUse.res 28:19 "start": { "character": 19, "line": 56 } }, "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 24, "line": 68 }, + "start": { "character": 19, "line": 68 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 13, "line": 34 }, + "start": { "character": 8, "line": 34 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 22, "line": 46 }, + "start": { "character": 17, "line": 46 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 23, "line": 150 }, + "start": { "character": 18, "line": 150 } + }, + "uri": "file:///AppliedFunctor.res" } ] TypeDefinition src/AppliedFunctorUse.res 33:4 { "range": { - "end": { "character": 23, "line": 49 }, - "start": { "character": 2, "line": 49 } + "end": { "character": 23, "line": 50 }, + "start": { "character": 2, "line": 50 } }, "uri": "file:///AppliedFunctor.res" } @@ -141,8 +204,8 @@ TypeDefinition src/AppliedFunctorUse.res 33:4 Definition src/AppliedFunctorUse.res 36:11 { "range": { - "end": { "character": 17, "line": 49 }, - "start": { "character": 16, "line": 49 } + "end": { "character": 17, "line": 50 }, + "start": { "character": 16, "line": 50 } }, "uri": "file:///AppliedFunctor.res" } @@ -150,8 +213,8 @@ Definition src/AppliedFunctorUse.res 36:11 Definition src/AppliedFunctorUse.res 39:17 { "range": { - "end": { "character": 24, "line": 50 }, - "start": { "character": 21, "line": 50 } + "end": { "character": 24, "line": 52 }, + "start": { "character": 21, "line": 52 } }, "uri": "file:///AppliedFunctor.res" } @@ -203,6 +266,34 @@ References src/AppliedFunctorUse.res 56:12 "start": { "character": 12, "line": 56 } }, "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 18, "line": 68 }, + "start": { "character": 12, "line": 68 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 15, "line": 32 }, + "start": { "character": 9, "line": 32 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 16, "line": 46 }, + "start": { "character": 10, "line": 46 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 17, "line": 150 }, + "start": { "character": 11, "line": 150 } + }, + "uri": "file:///AppliedFunctor.res" } ] @@ -221,6 +312,346 @@ References src/AppliedFunctorUse.res 59:15 "start": { "character": 15, "line": 59 } }, "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 20, "line": 65 }, + "start": { "character": 15, "line": 65 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 12, "line": 50 }, + "start": { "character": 7, "line": 50 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 19, "line": 57 }, + "start": { "character": 14, "line": 57 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 20, "line": 153 }, + "start": { "character": 15, "line": 153 } + }, + "uri": "file:///AppliedFunctor.res" + } +] + +Rename src/AppliedFunctorUse.res 62:16 addItem +[ + { + "edits": [ + { + "newText": "addItem", + "range": { + "end": { "character": 18, "line": 5 }, + "start": { "character": 15, "line": 5 } + } + }, + { + "newText": "addItem", + "range": { + "end": { "character": 19, "line": 17 }, + "start": { "character": 16, "line": 17 } + } + }, + { + "newText": "addItem", + "range": { + "end": { "character": 19, "line": 62 }, + "start": { "character": 16, "line": 62 } + } + } + ], + "textDocument": { "uri": "file:///AppliedFunctorUse.res", "version": 0 } + }, + { + "edits": [ + { + "newText": "addItem", + "range": { + "end": { "character": 9, "line": 7 }, + "start": { "character": 6, "line": 7 } + } + }, + { + "newText": "addItem", + "range": { + "end": { "character": 18, "line": 16 }, + "start": { "character": 15, "line": 16 } + } + }, + { + "newText": "addItem", + "range": { + "end": { "character": 19, "line": 19 }, + "start": { "character": 16, "line": 19 } + } + }, + { + "newText": "addItem", + "range": { + "end": { "character": 19, "line": 22 }, + "start": { "character": 16, "line": 22 } + } + } + ], + "textDocument": { "uri": "file:///AppliedFunctor.res", "version": 0 } + } +] + +Rename src/AppliedFunctorUse.res 65:15 location +[ + { + "edits": [ + { + "newText": "location", + "range": { + "end": { "character": 19, "line": 33 }, + "start": { "character": 14, "line": 33 } + } + }, + { + "newText": "location", + "range": { + "end": { "character": 20, "line": 59 }, + "start": { "character": 15, "line": 59 } + } + }, + { + "newText": "location", + "range": { + "end": { "character": 20, "line": 65 }, + "start": { "character": 15, "line": 65 } + } + } + ], + "textDocument": { "uri": "file:///AppliedFunctorUse.res", "version": 0 } + }, + { + "edits": [ + { + "newText": "location", + "range": { + "end": { "character": 12, "line": 50 }, + "start": { "character": 7, "line": 50 } + } + }, + { + "newText": "location", + "range": { + "end": { "character": 19, "line": 57 }, + "start": { "character": 14, "line": 57 } + } + }, + { + "newText": "location", + "range": { + "end": { "character": 20, "line": 153 }, + "start": { "character": 15, "line": 153 } + } + } + ], + "textDocument": { "uri": "file:///AppliedFunctor.res", "version": 0 } + } +] + +Rename src/AppliedFunctorUse.res 68:19 renamedValue +[ + { + "edits": [ + { + "newText": "renamedValue", + "range": { + "end": { "character": 23, "line": 22 }, + "start": { "character": 18, "line": 22 } + } + }, + { + "newText": "renamedValue", + "range": { + "end": { "character": 24, "line": 25 }, + "start": { "character": 19, "line": 25 } + } + }, + { + "newText": "renamedValue", + "range": { + "end": { "character": 24, "line": 28 }, + "start": { "character": 19, "line": 28 } + } + }, + { + "newText": "renamedValue", + "range": { + "end": { "character": 24, "line": 56 }, + "start": { "character": 19, "line": 56 } + } + }, + { + "newText": "renamedValue", + "range": { + "end": { "character": 24, "line": 68 }, + "start": { "character": 19, "line": 68 } + } + } + ], + "textDocument": { "uri": "file:///AppliedFunctorUse.res", "version": 0 } + }, + { + "edits": [ + { + "newText": "renamedValue", + "range": { + "end": { "character": 13, "line": 34 }, + "start": { "character": 8, "line": 34 } + } + }, + { + "newText": "renamedValue", + "range": { + "end": { "character": 22, "line": 46 }, + "start": { "character": 17, "line": 46 } + } + }, + { + "newText": "renamedValue", + "range": { + "end": { "character": 23, "line": 150 }, + "start": { "character": 18, "line": 150 } + } + } + ], + "textDocument": { "uri": "file:///AppliedFunctor.res", "version": 0 } + } +] + +References src/AppliedFunctorUse.res 71:12 +[ + { + "range": { + "end": { "character": 24, "line": 33 }, + "start": { "character": 23, "line": 33 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 12, "line": 36 }, + "start": { "character": 11, "line": 36 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 25, "line": 59 }, + "start": { "character": 24, "line": 59 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 25, "line": 65 }, + "start": { "character": 24, "line": 65 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 13, "line": 71 }, + "start": { "character": 12, "line": 71 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 17, "line": 50 }, + "start": { "character": 16, "line": 50 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 24, "line": 57 }, + "start": { "character": 23, "line": 57 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 12, "line": 58 }, + "start": { "character": 11, "line": 58 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 12, "line": 64 }, + "start": { "character": 11, "line": 64 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 25, "line": 153 }, + "start": { "character": 24, "line": 153 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 14, "line": 156 }, + "start": { "character": 13, "line": 156 } + }, + "uri": "file:///AppliedFunctor.res" + } +] + +Rename src/AppliedFunctorUse.res 74:18 Square +[ + { + "edits": [ + { + "newText": "Square", + "range": { + "end": { "character": 20, "line": 39 }, + "start": { "character": 17, "line": 39 } + } + }, + { + "newText": "Square", + "range": { + "end": { "character": 21, "line": 74 }, + "start": { "character": 18, "line": 74 } + } + } + ], + "textDocument": { "uri": "file:///AppliedFunctorUse.res", "version": 0 } + }, + { + "edits": [ + { + "newText": "Square", + "range": { + "end": { "character": 24, "line": 52 }, + "start": { "character": 21, "line": 52 } + } + }, + { + "newText": "Square", + "range": { + "end": { "character": 22, "line": 61 }, + "start": { "character": 19, "line": 61 } + } + } + ], + "textDocument": { "uri": "file:///AppliedFunctor.res", "version": 0 } } ] From ff8f5bef7b4c4905065978c09730ed08e02fbead Mon Sep 17 00:00:00 2001 From: Christoph Knittel Date: Thu, 29 Oct 2026 18:03:48 +0000 Subject: [PATCH 12/12] Follow a functor's aliases to its file for the members it creates Review fix: a functor from another file applied through a local alias, such as `module Make = AppliedFunctor.MakeSet` then `Make(IntOrd)`, kept the alias as the members' source, so Go to Definition and type-definition links pointed into the caller at the functor's line numbers, and uses didn't reach the functor's references and renames. The source is now the path the compiler resolved the alias chain to, through modules too. Analysis modified (go to definition of an applied member): Example: module Make = AppliedFunctor.MakeSet module Aliased = Make(AppliedFunctor.IntOrd) let a = Aliased.add([], 1) Before (text): definition of `add` file:///AppliedFunctorUse.res 7:6-7:9 After (text): definition of `add` file:///AppliedFunctor.res 7:6-7:9 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Kr4cjoUi2f5VpMvE6wT9bo Signed-off-by: Christoph Knittel --- analysis/src/process_cmt.ml | 4 + .../tests/src/AppliedFunctorUse.res | 27 ++++ .../tests/src/expected/AppliedFunctor.res.txt | 63 ++++++++ .../src/expected/AppliedFunctorUse.res.txt | 152 ++++++++++++++++++ 4 files changed, 246 insertions(+) diff --git a/analysis/src/process_cmt.ml b/analysis/src/process_cmt.ml index 8e613ce3d2..730c59aad4 100644 --- a/analysis/src/process_cmt.ml +++ b/analysis/src/process_cmt.ml @@ -503,6 +503,10 @@ let rec get_module_path mod_desc = | Tmod_functor (_ident, _argName, _maybeType, _resultExpr) -> None | Tmod_apply (functor_, _arg, _coercion) -> get_module_path functor_.mod_desc | Tmod_unpack (_expr, _moduleType) -> None + (* A module alias used as a functor or included is wrapped with the path + the compiler resolved its chain of aliases to: the module's own file. *) + | Tmod_constraint (_expr, _typ, _constraint, Tcoerce_alias (path, _)) -> + Some path | Tmod_constraint (expr, _typ, _constraint, _coercion) -> get_module_path expr.mod_desc diff --git a/tests/analysis_tests/tests/src/AppliedFunctorUse.res b/tests/analysis_tests/tests/src/AppliedFunctorUse.res index edc5f79112..a25765ea10 100644 --- a/tests/analysis_tests/tests/src/AppliedFunctorUse.res +++ b/tests/analysis_tests/tests/src/AppliedFunctorUse.res @@ -74,3 +74,30 @@ let px2 = p.x let box2 = Shapes.Box(2) // ^ren Square + +module Make = AppliedFunctor.MakeSet + +module Aliased = Make(AppliedFunctor.IntOrd) + +let a = Aliased.add([], 1) +// ^def + +let av: Aliased.t = [] +// ^typ + +module Make2 = Make + +module Functors = { + module Make = Make2 +} + +module Chained = Functors.Make(AppliedFunctor.IntOrd) + +let c = Chained.add([], 1) +// ^def + +let cv: Chained.t = [] +// ^typ + +let a2 = Aliased.add([], 2) +// ^ref diff --git a/tests/analysis_tests/tests/src/expected/AppliedFunctor.res.txt b/tests/analysis_tests/tests/src/expected/AppliedFunctor.res.txt index 7566834978..fc6160221a 100644 --- a/tests/analysis_tests/tests/src/expected/AppliedFunctor.res.txt +++ b/tests/analysis_tests/tests/src/expected/AppliedFunctor.res.txt @@ -21,6 +21,27 @@ References src/AppliedFunctor.res 7:7 }, "uri": "file:///AppliedFunctorUse.res" }, + { + "range": { + "end": { "character": 19, "line": 81 }, + "start": { "character": 16, "line": 81 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 19, "line": 95 }, + "start": { "character": 16, "line": 95 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 20, "line": 101 }, + "start": { "character": 17, "line": 101 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, { "range": { "end": { "character": 9, "line": 7 }, @@ -82,6 +103,27 @@ References src/AppliedFunctor.res 19:16 }, "uri": "file:///AppliedFunctorUse.res" }, + { + "range": { + "end": { "character": 19, "line": 81 }, + "start": { "character": 16, "line": 81 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 19, "line": 95 }, + "start": { "character": 16, "line": 95 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 20, "line": 101 }, + "start": { "character": 17, "line": 101 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, { "range": { "end": { "character": 9, "line": 7 }, @@ -136,6 +178,27 @@ Rename src/AppliedFunctor.res 22:16 insert "end": { "character": 19, "line": 62 }, "start": { "character": 16, "line": 62 } } + }, + { + "newText": "insert", + "range": { + "end": { "character": 19, "line": 81 }, + "start": { "character": 16, "line": 81 } + } + }, + { + "newText": "insert", + "range": { + "end": { "character": 19, "line": 95 }, + "start": { "character": 16, "line": 95 } + } + }, + { + "newText": "insert", + "range": { + "end": { "character": 20, "line": 101 }, + "start": { "character": 17, "line": 101 } + } } ], "textDocument": { "uri": "file:///AppliedFunctorUse.res", "version": 0 } diff --git a/tests/analysis_tests/tests/src/expected/AppliedFunctorUse.res.txt b/tests/analysis_tests/tests/src/expected/AppliedFunctorUse.res.txt index 662a6fac4f..3936096ab2 100644 --- a/tests/analysis_tests/tests/src/expected/AppliedFunctorUse.res.txt +++ b/tests/analysis_tests/tests/src/expected/AppliedFunctorUse.res.txt @@ -84,6 +84,27 @@ References src/AppliedFunctorUse.res 17:16 }, "uri": "file:///AppliedFunctorUse.res" }, + { + "range": { + "end": { "character": 19, "line": 81 }, + "start": { "character": 16, "line": 81 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 19, "line": 95 }, + "start": { "character": 16, "line": 95 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 20, "line": 101 }, + "start": { "character": 17, "line": 101 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, { "range": { "end": { "character": 9, "line": 7 }, @@ -367,6 +388,27 @@ Rename src/AppliedFunctorUse.res 62:16 addItem "end": { "character": 19, "line": 62 }, "start": { "character": 16, "line": 62 } } + }, + { + "newText": "addItem", + "range": { + "end": { "character": 19, "line": 81 }, + "start": { "character": 16, "line": 81 } + } + }, + { + "newText": "addItem", + "range": { + "end": { "character": 19, "line": 95 }, + "start": { "character": 16, "line": 95 } + } + }, + { + "newText": "addItem", + "range": { + "end": { "character": 20, "line": 101 }, + "start": { "character": 17, "line": 101 } + } } ], "textDocument": { "uri": "file:///AppliedFunctorUse.res", "version": 0 } @@ -655,3 +697,113 @@ Rename src/AppliedFunctorUse.res 74:18 Square } ] +Definition src/AppliedFunctorUse.res 81:17 +{ + "range": { + "end": { "character": 9, "line": 7 }, + "start": { "character": 6, "line": 7 } + }, + "uri": "file:///AppliedFunctor.res" +} + +TypeDefinition src/AppliedFunctorUse.res 84:4 +{ + "range": { + "end": { "character": 21, "line": 6 }, + "start": { "character": 2, "line": 6 } + }, + "uri": "file:///AppliedFunctor.res" +} + +Definition src/AppliedFunctorUse.res 95:17 +{ + "range": { + "end": { "character": 9, "line": 7 }, + "start": { "character": 6, "line": 7 } + }, + "uri": "file:///AppliedFunctor.res" +} + +TypeDefinition src/AppliedFunctorUse.res 98:4 +{ + "range": { + "end": { "character": 21, "line": 6 }, + "start": { "character": 2, "line": 6 } + }, + "uri": "file:///AppliedFunctor.res" +} + +References src/AppliedFunctorUse.res 101:18 +[ + { + "range": { + "end": { "character": 18, "line": 5 }, + "start": { "character": 15, "line": 5 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 19, "line": 17 }, + "start": { "character": 16, "line": 17 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 19, "line": 62 }, + "start": { "character": 16, "line": 62 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 19, "line": 81 }, + "start": { "character": 16, "line": 81 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 19, "line": 95 }, + "start": { "character": 16, "line": 95 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 20, "line": 101 }, + "start": { "character": 17, "line": 101 } + }, + "uri": "file:///AppliedFunctorUse.res" + }, + { + "range": { + "end": { "character": 9, "line": 7 }, + "start": { "character": 6, "line": 7 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 18, "line": 16 }, + "start": { "character": 15, "line": 16 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 19, "line": 19 }, + "start": { "character": 16, "line": 19 } + }, + "uri": "file:///AppliedFunctor.res" + }, + { + "range": { + "end": { "character": 19, "line": 22 }, + "start": { "character": 16, "line": 22 } + }, + "uri": "file:///AppliedFunctor.res" + } +] +