Skip to content

Commit fdc6011

Browse files
committed
improve error messages
Signed-off-by: ArthurW <arthur@tarides.com>
1 parent 2b43394 commit fdc6011

File tree

7 files changed

+173
-46
lines changed

7 files changed

+173
-46
lines changed

src/dune_rules/lib.ml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -608,9 +608,9 @@ module Parameterized = struct
608608
List.sort arguments ~compare:(fun (param, _) (param', _) -> compare param param')
609609
;;
610610

611-
let instantiate ~loc named_lib args ~parent_parameters =
611+
let instantiate ~loc lib args ~parent_parameters =
612612
let open Resolve.O in
613-
let* lib = named_lib
613+
let* lib = lib
614614
and* args = make_arguments args in
615615
let* lib = apply_arguments ~ignore_extra:false lib args in
616616
let+ () =

src/dune_rules/parameterized_rules.ml

Lines changed: 153 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -398,38 +398,125 @@ let gen_rules ~sctx ~dir ~scope rest =
398398
type instance =
399399
{ new_name : Module_name.t
400400
; lib_name : Module_name.t
401-
; args : (Module_name.t * Module_name.t) list
401+
; args : (Loc.t * Module_name.t * Module_name.t) list
402+
; loc : Loc.t
402403
}
403404

404405
type instances =
405406
| Simple of instance
406-
| Wrapped of Module_name.t * instance list
407+
| Wrapped of Loc.t * Module_name.t * instance list
408+
409+
module Errors = struct
410+
let make_resolve ?loc ?hints paragraphs =
411+
Resolve.fail
412+
(User_error.make
413+
?loc
414+
?hints
415+
paragraphs
416+
~annots:(User_message.Annots.singleton User_message.Annots.needs_stack_trace ()))
417+
;;
418+
419+
let make ?loc ?hints paragraphs = Memo.return @@ make_resolve ?loc ?hints paragraphs
420+
421+
let library_not_found ~loc name =
422+
make ~loc [ Pp.textf "Library parameter %S not found." (Lib_name.to_string name) ]
423+
;;
424+
425+
let duplicate_parameters ~loc ~param arg arg' =
426+
make
427+
~loc
428+
[ Pp.textf
429+
"Duplicate arguments %s and %s for parameter %s."
430+
(Lib_name.to_string (Lib.name arg))
431+
(Lib_name.to_string (Lib.name arg'))
432+
(Lib_name.to_string (Lib.name param))
433+
]
434+
;;
435+
436+
let missing_implements ~loc p =
437+
let name = Lib_name.to_string (Lib.name p) in
438+
make ~loc [ Pp.textf "Library %S does not implement a library parameter." name ]
439+
;;
440+
441+
let unexpected_argument ?loc param arg =
442+
make
443+
?loc
444+
[ Pp.textf
445+
"Argument %s implements unexpected parameter %s"
446+
(Lib_name.to_string (Lib.name arg))
447+
(Lib_name.to_string (Lib.name param))
448+
]
449+
~hints:[ Pp.text "Remove this argument" ]
450+
;;
451+
452+
let new_name_already_used ?loc name =
453+
make
454+
?loc
455+
[ Pp.textf "The instance name %s is already used." (Module_name.to_string name) ]
456+
;;
457+
458+
let module_name_already_used ?loc name =
459+
make
460+
?loc
461+
[ Pp.textf "Module name %s has already been used." (Module_name.to_string name) ]
462+
;;
463+
end
407464

408465
let instances ~sctx ~db (deps : Lib_dep.t list) =
409466
let open Resolve.Memo.O in
410467
Resolve.Memo.List.concat_map deps ~f:(function
411468
| Lib_dep.Direct _ | Lib_dep.Re_export _ | Lib_dep.Select _ -> Resolve.Memo.return []
412-
| Lib_dep.Instantiate { loc = _; lib; arguments; new_name } ->
413-
let+ entry_names =
414-
let* lib = Resolve.Memo.lift_memo @@ Lib.DB.find db lib in
469+
| Lib_dep.Instantiate { loc; lib = lib_name; arguments; new_name } ->
470+
let* lib = Resolve.Memo.lift_memo @@ Lib.DB.find db lib_name in
471+
let lib =
415472
match lib with
416-
| None -> Resolve.Memo.return []
417-
| Some lib -> Root_module.entry_module_names sctx lib
473+
| None -> Code_error.raise "lib not found" [ "lib", Lib_name.to_dyn lib_name ]
474+
| Some lib -> lib
475+
in
476+
let* expected_params =
477+
let* parameters = Lib.parameters lib in
478+
let+ module_names =
479+
Resolve.Memo.List.filter_map parameters ~f:Lib.main_module_name
480+
in
481+
Module_name.Map.of_list_map_exn module_names ~f:(fun m -> m, [])
482+
in
483+
let+ entry_names = Root_module.entry_module_names sctx lib
418484
and+ args =
419-
Resolve.Memo.List.filter_map arguments ~f:(fun (_loc, arg_name) ->
420-
let* arg = Resolve.Memo.lift_memo @@ Lib.DB.find db arg_name in
421-
match arg with
422-
| None -> Resolve.Memo.return None
423-
| Some arg ->
424-
(match Lib.implements arg with
425-
| None -> Resolve.Memo.return None
426-
| Some param ->
427-
let* param = param in
428-
let+ param_name = Lib.main_module_name param
429-
and+ arg_name = Lib.main_module_name arg in
430-
(match param_name, arg_name with
431-
| Some param_name, Some arg_name -> Some (param_name, arg_name)
432-
| _ -> None)))
485+
Resolve.Memo.List.fold_left
486+
arguments
487+
~init:expected_params
488+
~f:(fun args (loc, arg_name) ->
489+
let* arg = Resolve.Memo.lift_memo @@ Lib.DB.find db arg_name in
490+
match arg with
491+
| None -> Errors.library_not_found ~loc arg_name
492+
| Some arg ->
493+
(match Lib.implements arg with
494+
| None -> Errors.missing_implements ~loc arg
495+
| Some param ->
496+
let* param = param in
497+
let* param_name = Lib.main_module_name param
498+
and* arg_name = Lib.main_module_name arg in
499+
(match param_name, arg_name with
500+
| Some param_name, Some arg_name ->
501+
(match Module_name.Map.find args param_name with
502+
| Some [] ->
503+
Resolve.Memo.return
504+
@@ Module_name.Map.add_multi args param_name (loc, arg, arg_name)
505+
| None -> Errors.unexpected_argument ~loc arg param
506+
| Some ((_, existing, _) :: _) ->
507+
Errors.duplicate_parameters ~loc ~param existing arg)
508+
| None, None | Some _, None | None, Some _ ->
509+
Errors.missing_implements ~loc arg)))
510+
in
511+
let args =
512+
Module_name.Map.foldi args ~init:[] ~f:(fun param arg_opt acc ->
513+
match arg_opt with
514+
| [] -> acc
515+
| [ (loc, _lib, arg) ] -> (loc, param, arg) :: acc
516+
| (_, arg, _) :: (_, arg', _) :: _ ->
517+
Code_error.raise
518+
"duplicate arguments were already reported"
519+
[ "arg", Lib.to_dyn arg; "arg'", Lib.to_dyn arg' ])
433520
in
434521
(match entry_names with
435522
| [] -> []
@@ -439,15 +526,55 @@ let instances ~sctx ~db (deps : Lib_dep.t list) =
439526
| None -> entry_name
440527
| Some new_name -> new_name
441528
in
442-
[ Simple { new_name; lib_name = entry_name; args } ]
529+
[ Simple { new_name; lib_name = entry_name; args; loc } ]
443530
| _ :: _ :: _ ->
444531
let instances =
445532
List.map entry_names ~f:(fun name ->
446-
{ new_name = name; lib_name = name; args })
533+
{ new_name = name; lib_name = name; args; loc })
447534
in
448535
(match new_name with
449536
| None -> List.map ~f:(fun i -> Simple i) instances
450-
| Some new_name -> [ Wrapped (new_name, instances) ])))
537+
| Some new_name -> [ Wrapped (loc, new_name, instances) ])))
538+
;;
539+
540+
let check_instance known_names instance =
541+
if Module_name.Set.mem known_names instance.new_name
542+
then Errors.new_name_already_used ~loc:instance.loc instance.new_name
543+
else if Module_name.Set.mem known_names instance.lib_name
544+
then Errors.module_name_already_used ~loc:instance.loc instance.new_name
545+
else
546+
let open Resolve.Memo.O in
547+
let+ () =
548+
Resolve.Memo.List.iter instance.args ~f:(fun (loc, _param_name, arg_name) ->
549+
if Module_name.Set.mem known_names arg_name
550+
then Errors.module_name_already_used ~loc instance.new_name
551+
else Resolve.Memo.return ())
552+
in
553+
Module_name.Set.add known_names instance.new_name
554+
;;
555+
556+
let check_instances instances =
557+
let open Resolve.Memo.O in
558+
Resolve.Memo.List.fold_left
559+
instances
560+
~init:Module_name.Set.empty
561+
~f:(fun acc -> function
562+
| Simple instance -> check_instance acc instance
563+
| Wrapped (loc, wrapped_name, instances) ->
564+
if Module_name.Set.mem acc wrapped_name
565+
then Errors.new_name_already_used ~loc wrapped_name
566+
else
567+
let+ _sub_definitions : Module_name.Set.t =
568+
Resolve.Memo.List.fold_left instances ~init:acc ~f:check_instance
569+
in
570+
Module_name.Set.add acc wrapped_name)
571+
;;
572+
573+
let instances ~sctx ~db deps =
574+
let open Resolve.Memo.O in
575+
let* instances = instances ~sctx ~db deps in
576+
let+ _ = check_instances instances in
577+
instances
451578
;;
452579

453580
let print_instance b indent instance =
@@ -458,7 +585,7 @@ let print_instance b indent instance =
458585
(Module_name.to_string instance.new_name)
459586
(Module_name.to_string instance.lib_name)
460587
(String.concat ~sep:""
461-
@@ List.map instance.args ~f:(fun (param_name, arg_name) ->
588+
@@ List.map instance.args ~f:(fun (_loc, param_name, arg_name) ->
462589
Printf.sprintf
463590
"(%s)(%s)"
464591
(Module_name.to_string param_name)
@@ -469,7 +596,7 @@ let print_instances b instances =
469596
List.iter instances ~f:(fun instances ->
470597
match instances with
471598
| Simple instance -> print_instance b "" instance
472-
| Wrapped (new_name, instances) ->
599+
| Wrapped (_loc, new_name, instances) ->
473600
Printf.bprintf b "\nmodule %s = struct" (Module_name.to_string new_name);
474601
List.iter instances ~f:(print_instance b " ");
475602
Printf.bprintf b "\nend\n")
Binary file not shown.

test/blackbox-tests/test-cases/oxcaml/install-parameterized.t/external/lib/dune

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
(public_name external.lib)
33
(name lib)
44
(libraries
5-
(paramlib impl)
5+
(paramlib impl :as paramlib1)
66
(paramlib impl2 :as paramlib2)))
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
let test () = String.uppercase_ascii (Paramlib.v ^ " " ^ Paramlib2.v)
1+
let test () = String.uppercase_ascii (Paramlib1.v ^ " " ^ Paramlib2.v)

test/blackbox-tests/test-cases/oxcaml/install-parameterized.t/run.t

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ and local implementations:
4848
> print_endline Paramlib_impl.v ;
4949
> print_endline Paramlib_otherext.v ;
5050
> print_endline (Lib.test ()) ;
51-
> print_endline (Unwrapped_a.a ^ "," ^ Unwrapped_b.b) ;
51+
> print_endline Unwrap_lib.(Unwrapped_a.a ^ "," ^ Unwrapped_b.b) ;
5252
> print_endline (Rewrap.Unwrapped_a.a ^ "," ^ Rewrap.Unwrapped_b.b) ;
5353
> print_endline Other_ext.v
5454
> EOF
@@ -58,7 +58,7 @@ and local implementations:
5858
> (external.paramlib external.impl :as paramlib_impl)
5959
> (external.paramlib other_ext :as paramlib_otherext)
6060
> external.lib ; has instances internally
61-
> (external.unwrapped_lib external.impl)
61+
> (external.unwrapped_lib external.impl :as unwrap_lib)
6262
> (external.unwrapped_lib other_ext :as rewrap)
6363
> other_ext))
6464
> EOF

test/blackbox-tests/test-cases/oxcaml/instantiate-parameterized.t

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ It's an error for the binary to partially instantiate `lib_ab`:
7171
3 | (libraries (lib_ab b_impl))) ; missing a_impl
7272
^^^^^^
7373
Error: Parameter "project.a" is missing.
74+
-> required by _build/default/bin/bin.exe
7475
-> required by _build/install/default/bin/project.bin
7576
Hint: Pass an argument implementing project.a to the dependency, or add
7677
(parameters project.a)
@@ -91,11 +92,11 @@ overlapping modules)
9192
> EOF
9293

9394
$ dune exec project.bin
94-
File "bin/.bin.eobjs/dune__exe.ml-gen", line 7, characters 0-75:
95-
7 | module Lib_ab = Lib_ab(A)(A_impl)(B)(B_impl) [@jane.non_erasable.instances]
96-
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
97-
Error: Multiple definition of the module name Lib_ab.
98-
Names must be unique in a given structure or signature.
95+
File "bin/dune", line 7, characters 5-11:
96+
7 | (lib_ab a_impl b_impl)))
97+
^^^^^^
98+
Error: The instance name Lib_ab is already used.
99+
-> required by _build/install/default/bin/project.bin
99100
[1]
100101

101102
We add another way to implement the parameter `b` from the parameter `a`:
@@ -126,10 +127,9 @@ dependencies, because its parameter `b` is missing:
126127
File "bin/dune", line 6, characters 19-25:
127128
6 | (lib_ab a_impl a_of_b)))
128129
^^^^^^
129-
Error: Parameter "project.b" is missing.
130+
Error: Duplicate arguments project.a_impl and project.a_of_b for parameter
131+
project.a.
130132
-> required by _build/install/default/bin/project.bin
131-
Hint: Pass an argument implementing project.b to the dependency, or add
132-
(parameters project.b)
133133
[1]
134134

135135
However `lib_ab` can depend on `a_of_b`, such that the parameter `b` will be
@@ -189,9 +189,9 @@ It's an error to provide a non-required parameter:
189189
File "bin/dune", line 4, characters 15-21:
190190
4 | (lib_apply a_impl b_impl :as lib_ab)))
191191
^^^^^^
192-
Error: Unexpected argument "project.a_impl"
192+
Error: Argument project.a implements unexpected parameter project.a_impl
193193
-> required by _build/install/default/bin/project.bin
194-
Hint: Remove the extra argument
194+
Hint: Remove this argument
195195
[1]
196196

197197
Given another implementation of a parameter,
@@ -216,9 +216,9 @@ which one to use:
216216
File "bin/dune", line 4, characters 22-29:
217217
4 | (lib_apply b_impl b_impl2)))
218218
^^^^^^^
219-
Error: Unexpected argument "project.b_impl2"
219+
Error: Duplicate arguments project.b_impl and project.b_impl2 for parameter
220+
project.b.
220221
-> required by _build/install/default/bin/project.bin
221-
Hint: Remove the extra argument
222222
[1]
223223

224224
Same error if the argument is repeated:
@@ -233,9 +233,9 @@ Same error if the argument is repeated:
233233
File "bin/dune", line 4, characters 22-28:
234234
4 | (lib_apply b_impl b_impl)))
235235
^^^^^^
236-
Error: Unexpected argument "project.b_impl"
236+
Error: Duplicate arguments project.b_impl and project.b_impl for parameter
237+
project.b.
237238
-> required by _build/install/default/bin/project.bin
238-
Hint: Remove the extra argument
239239
[1]
240240

241241
We can instantiate the same library multiple times by giving it different names:

0 commit comments

Comments
 (0)