Skip to content

Commit 09ec615

Browse files
committed
more improvements to completions involving modules
1 parent 18ff979 commit 09ec615

File tree

4 files changed

+366
-14
lines changed

4 files changed

+366
-14
lines changed

analysis/src/CompletionFrontEnd.ml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1699,6 +1699,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
16991699
in
17001700
let module_expr (iterator : Ast_iterator.iterator)
17011701
(me : Parsetree.module_expr) =
1702+
let processed = ref false in
17021703
(match me.pmod_desc with
17031704
| Pmod_ident lid when lid.loc |> Loc.hasPos ~pos:posBeforeCursor ->
17041705
let lidPath = flattenLidCheckDot lid in
@@ -1710,8 +1711,17 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
17101711
setResult
17111712
(Cpath
17121713
(CPId {loc = lid.loc; path = lidPath; completionContext = Module}))
1714+
| Pmod_functor (name, maybeType, body) ->
1715+
let oldScope = !scope in
1716+
scope := !scope |> Scope.addModule ~name:name.txt ~loc:name.loc;
1717+
(match maybeType with
1718+
| None -> ()
1719+
| Some mt -> iterator.module_type iterator mt);
1720+
iterator.module_expr iterator body;
1721+
scope := oldScope;
1722+
processed := true
17131723
| _ -> ());
1714-
Ast_iterator.default_iterator.module_expr iterator me
1724+
if not !processed then Ast_iterator.default_iterator.module_expr iterator me
17151725
in
17161726
let module_type (iterator : Ast_iterator.iterator)
17171727
(mt : Parsetree.module_type) =

analysis/src/ProcessCmt.ml

Lines changed: 73 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,8 @@ let rec forStructureItem ~(env : SharedTypes.Env.t) ~(exported : Exported.t)
521521
(fun {Typedtree.vb_pat; vb_attributes} ->
522522
handlePattern vb_attributes vb_pat)
523523
bindings;
524+
bindings
525+
|> List.iter (fun {Typedtree.vb_expr} -> scanLetModules ~env vb_expr);
524526
!items
525527
| Tstr_module
526528
{mb_id; mb_attributes; mb_loc; mb_name = name; mb_expr = {mod_desc}}
@@ -630,16 +632,14 @@ and forModule ~env mod_desc moduleName =
630632
| Tmod_functor (ident, argName, maybeType, resultExpr) ->
631633
(match maybeType with
632634
| None -> ()
633-
| Some t -> (
634-
match forTreeModuleType ~name:argName.txt ~env t with
635-
| None -> ()
636-
| Some kind ->
637-
let stamp = Ident.binding_time ident in
638-
let declared =
639-
ProcessAttributes.newDeclared ~item:kind ~name:argName
640-
~extent:t.Typedtree.mty_loc ~stamp ~modulePath:NotVisible false []
641-
in
642-
Stamps.addModule env.stamps stamp declared));
635+
| Some t ->
636+
let kind = forTypeModule ~name:argName.txt ~env t.mty_type in
637+
let stamp = Ident.binding_time ident in
638+
let declared =
639+
ProcessAttributes.newDeclared ~item:kind ~name:argName
640+
~extent:argName.loc ~stamp ~modulePath:NotVisible false []
641+
in
642+
Stamps.addModule env.stamps stamp declared);
643643
forModule ~env resultExpr.mod_desc moduleName
644644
| Tmod_apply (functor_, _arg, _coercion) ->
645645
forModule ~env functor_.mod_desc moduleName
@@ -653,6 +653,69 @@ and forModule ~env mod_desc moduleName =
653653
let modTypeKind = forTypeModule ~name:moduleName ~env typ in
654654
Constraint (modKind, modTypeKind)
655655

656+
(*
657+
Walk a typed expression and register any `let module M = ...` bindings as local
658+
modules in stamps. This makes trailing-dot completion work for aliases like `M.`
659+
that are introduced inside expression scopes. The declared module is marked as
660+
NotVisible (non-exported) and the extent is the alias identifier location so
661+
scope lookups match precisely.
662+
*)
663+
and scanLetModules ~env (e : Typedtree.expression) =
664+
match e.exp_desc with
665+
| Texp_letmodule (id, name, mexpr, body) ->
666+
let stamp = Ident.binding_time id in
667+
let item = forModule ~env mexpr.mod_desc name.txt in
668+
let declared =
669+
ProcessAttributes.newDeclared ~item ~extent:name.loc ~name ~stamp
670+
~modulePath:NotVisible false []
671+
in
672+
Stamps.addModule env.stamps stamp declared;
673+
scanLetModules ~env body
674+
| Texp_let (_rf, bindings, body) ->
675+
List.iter (fun {Typedtree.vb_expr} -> scanLetModules ~env vb_expr) bindings;
676+
scanLetModules ~env body
677+
| Texp_apply {funct; args; _} ->
678+
scanLetModules ~env funct;
679+
args
680+
|> List.iter (function
681+
| _, Some e -> scanLetModules ~env e
682+
| _, None -> ())
683+
| Texp_tuple exprs -> List.iter (scanLetModules ~env) exprs
684+
| Texp_sequence (e1, e2) ->
685+
scanLetModules ~env e1;
686+
scanLetModules ~env e2
687+
| Texp_match (e, cases, exn_cases, _) ->
688+
scanLetModules ~env e;
689+
let scan_case {Typedtree.c_lhs = _; c_guard; c_rhs} =
690+
(match c_guard with
691+
| Some g -> scanLetModules ~env g
692+
| None -> ());
693+
scanLetModules ~env c_rhs
694+
in
695+
List.iter scan_case cases;
696+
List.iter scan_case exn_cases
697+
| Texp_function {case; _} ->
698+
let {Typedtree.c_lhs = _; c_guard; c_rhs} = case in
699+
(match c_guard with
700+
| Some g -> scanLetModules ~env g
701+
| None -> ());
702+
scanLetModules ~env c_rhs
703+
| Texp_try (e, cases) ->
704+
scanLetModules ~env e;
705+
cases
706+
|> List.iter (fun {Typedtree.c_lhs = _; c_guard; c_rhs} ->
707+
(match c_guard with
708+
| Some g -> scanLetModules ~env g
709+
| None -> ());
710+
scanLetModules ~env c_rhs)
711+
| Texp_ifthenelse (e1, e2, e3Opt) -> (
712+
scanLetModules ~env e1;
713+
scanLetModules ~env e2;
714+
match e3Opt with
715+
| Some e3 -> scanLetModules ~env e3
716+
| None -> ())
717+
| _ -> ()
718+
656719
and forStructure ~name ~env strItems =
657720
let exported = Exported.init () in
658721
let items =

tests/analysis_tests/tests/src/FirstClassModules.res

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,55 @@ let someFn = (~ctx: {"someModule": module(SomeModule)}) => {
1616

1717
let _ff = SomeModule.doStuff
1818
// ^hov
19+
20+
module M = CompletionFromModule.SomeModule
21+
// M.
22+
// ^com
23+
24+
// M.g
25+
// ^com
26+
()
27+
}
28+
29+
// Module type alias + unpack
30+
module type S2 = SomeModule
31+
let testAliasUnpack = (~ctx: {"someModule": module(SomeModule)}) => {
32+
let module(S2) = ctx["someModule"]
33+
// S2.
34+
// ^com
35+
()
36+
}
37+
// Functor param completion
38+
module Functor = (X: SomeModule) => {
39+
// X.
40+
// ^com
41+
let _u = X.doStuff
1942
}
43+
// First-class type hover without binding via module pattern
44+
let typeHover = (~ctx: {"someModule": module(SomeModule)}) => {
45+
let v: module(SomeModule) = ctx["someModule"]
46+
// ^hov
47+
()
48+
}
49+
// Nested unpack inside nested module
50+
module Outer = {
51+
let nested = (~ctx: {"someModule": module(SomeModule)}) => {
52+
let module(SomeModule) = ctx["someModule"]
53+
//SomeModule.
54+
// ^com
55+
()
56+
}
57+
}
58+
// Shadowing: inner binding should be used for completion
59+
let shadowing = (
60+
~ctx1: {"someModule": module(SomeModule)},
61+
~ctx2: {"someModule": module(SomeModule)},
62+
) => {
63+
let module(SomeModule) = ctx1["someModule"]
64+
{
65+
let module(SomeModule) = ctx2["someModule"]
66+
//SomeModule.
67+
// ^com
68+
()
69+
}
70+
}

0 commit comments

Comments
 (0)