Skip to content

Commit 54e5278

Browse files
authored
Merge pull request #73 from tatchi/avoid-raising-errors
Avoid raising errors
2 parents 1960006 + d03f272 commit 54e5278

File tree

3 files changed

+185
-121
lines changed

3 files changed

+185
-121
lines changed

src/ppx_import.ml

Lines changed: 168 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
module Tt = Ppx_types_migrate
22

3+
type error = {loc : Location.t; error : string}
4+
5+
exception Error of error
6+
7+
let raise_error ~loc error = raise (Error {loc; error})
8+
39
let lazy_env =
410
lazy
511
( (* It is important that the typing environment is not evaluated
@@ -66,9 +72,12 @@ let try_find_module_type ~loc env lid =
6672
Some
6773
( match modtype_decl.mtd_type with
6874
| None ->
69-
Location.raise_errorf ~loc
70-
"[%%import]: cannot access the signature of the abstract module %s"
71-
(string_of_lid lid)
75+
let error =
76+
Printf.sprintf
77+
"[%%import]: cannot access the signature of the abstract module %s"
78+
(string_of_lid lid)
79+
in
80+
raise_error ~loc error
7281
| Some module_type -> module_type )
7382
with Not_found -> None
7483

@@ -87,14 +96,24 @@ let open_module_type ~loc env lid module_type =
8796
match try_open_module_type env module_type with
8897
| Some sig_items -> sig_items
8998
| None ->
90-
Location.raise_errorf ~loc "[%%import]: cannot find the components of %s"
91-
(string_of_lid lid)
99+
let error =
100+
Printf.sprintf "[%%import]: cannot find the components of %s"
101+
(string_of_lid lid)
102+
in
103+
raise_error ~loc error
92104

93105
let locate_sig ~loc env lid =
94106
let head, path =
95-
match Ppxlib.Longident.flatten_exn lid with
96-
| head :: path -> (Longident.Lident head, path)
97-
| _ -> assert false
107+
try
108+
match Ppxlib.Longident.flatten_exn lid with
109+
| head :: path -> (Longident.Lident head, path)
110+
| _ -> assert false
111+
with Invalid_argument _ ->
112+
let error =
113+
Printf.sprintf "[%%import] cannot import a functor application %s"
114+
(string_of_lid lid)
115+
in
116+
raise_error ~loc error
98117
in
99118
let head_module_type =
100119
match
@@ -103,8 +122,10 @@ let locate_sig ~loc env lid =
103122
| Some mty, _ -> mty
104123
| None, (lazy (Some mty)) -> mty
105124
| None, (lazy None) ->
106-
Location.raise_errorf ~loc "[%%import]: cannot locate module %s"
107-
(string_of_lid lid)
125+
let error =
126+
Printf.sprintf "[%%import]: cannot locate module %s" (string_of_lid lid)
127+
in
128+
raise_error ~loc error
108129
in
109130
let get_sub_module_type (lid, module_type) path_item =
110131
let sig_items = open_module_type ~loc env lid module_type in
@@ -117,9 +138,11 @@ let locate_sig ~loc env lid =
117138
md_type
118139
| _ :: sig_items -> loop sig_items
119140
| [] ->
120-
Location.raise_errorf ~loc
121-
"[%%import]: cannot find the signature of %s in %s" path_item
122-
(string_of_lid lid)
141+
let error =
142+
Printf.sprintf "[%%import]: cannot find the signature of %s in %s"
143+
path_item (string_of_lid lid)
144+
in
145+
raise_error ~loc error
123146
in
124147
let sub_module_type =
125148
loop (List.map Compat.migrate_signature_item sig_items)
@@ -148,8 +171,11 @@ let get_type_decl ~loc sig_items parent_lid elem =
148171
in
149172
match try_get_tsig_item select_type ~loc sig_items elem with
150173
| None ->
151-
Location.raise_errorf "[%%import]: cannot find the type %s in %s" elem
152-
(string_of_lid parent_lid)
174+
let error =
175+
Printf.sprintf "[%%import]: cannot find the type %s in %s" elem
176+
(string_of_lid parent_lid)
177+
in
178+
raise_error ~loc error
153179
| Some decl -> decl
154180

155181
let get_modtype_decl ~loc sig_items parent_lid elem =
@@ -160,8 +186,11 @@ let get_modtype_decl ~loc sig_items parent_lid elem =
160186
in
161187
match try_get_tsig_item select_modtype ~loc sig_items elem with
162188
| None ->
163-
Location.raise_errorf "[%%import]: cannot find the module type %s in %s"
164-
elem (string_of_lid parent_lid)
189+
let error =
190+
Printf.sprintf "[%%import]: cannot find the module type %s in %s" elem
191+
(string_of_lid parent_lid)
192+
in
193+
raise_error ~loc error
165194
| Some decl -> decl
166195

167196
let longident_of_path = Untypeast.lident_of_path
@@ -239,10 +268,12 @@ let ptype_decl_of_ttype_decl ~manifest ~subst ptype_name
239268
ttype_decl.type_params ptype_args
240269
|> List.concat
241270
with Invalid_argument _ ->
242-
Location.raise_errorf ~loc:ptyp_loc
243-
"Imported type has %d parameter(s), but %d are passed"
244-
(List.length ttype_decl.type_params)
245-
(List.length ptype_args) )
271+
let error =
272+
Printf.sprintf "Imported type has %d parameter(s), but %d are passed"
273+
(List.length ttype_decl.type_params)
274+
(List.length ptype_args)
275+
in
276+
raise_error ~loc:ptyp_loc error )
246277
| None -> []
247278
| _ -> assert false
248279
in
@@ -337,8 +368,7 @@ let subst_of_manifest ({ptyp_attributes; ptyp_loc; _} : Ppxlib.core_type) =
337368
; ptyp_attributes = pexp_attributes
338369
; ptyp_desc = Ptyp_constr (dst, []) } )
339370
:: subst_of_expr rest
340-
| {pexp_loc; _} ->
341-
Location.raise_errorf ~loc:pexp_loc "Invalid [@with] syntax"
371+
| {pexp_loc; _} -> raise_error ~loc:pexp_loc "Invalid [@with] syntax"
342372
in
343373
let find_attr s attrs =
344374
try
@@ -348,18 +378,25 @@ let subst_of_manifest ({ptyp_attributes; ptyp_loc; _} : Ppxlib.core_type) =
348378
match find_attr "with" ptyp_attributes with
349379
| None -> []
350380
| Some (PStr [{pstr_desc = Pstr_eval (expr, []); _}]) -> subst_of_expr expr
351-
| Some _ -> Location.raise_errorf ~loc:ptyp_loc "Invalid [@with] syntax"
381+
| Some _ -> raise_error ~loc:ptyp_loc "Invalid [@with] syntax"
352382

353383
let uncapitalize = String.uncapitalize_ascii
354384

355-
let is_self_reference ~input_name lid =
385+
let is_self_reference ~input_name ~loc lid =
356386
let fn =
357387
input_name |> Filename.basename |> Filename.chop_extension |> uncapitalize
358388
in
359389
match lid with
360-
| Ppxlib.Ldot _ ->
361-
let mn = Ppxlib.Longident.flatten_exn lid |> List.hd |> uncapitalize in
362-
fn = mn
390+
| Ppxlib.Ldot _ -> (
391+
try
392+
let mn = Ppxlib.Longident.flatten_exn lid |> List.hd |> uncapitalize in
393+
fn = mn
394+
with Invalid_argument _ ->
395+
let error =
396+
Printf.sprintf "[%%import] cannot import a functor application %s"
397+
(string_of_lid lid)
398+
in
399+
raise_error ~loc error )
363400
| _ -> false
364401

365402
let type_declaration ~tool_name ~input_name (type_decl : Ppxlib.type_declaration)
@@ -370,47 +407,56 @@ let type_declaration ~tool_name ~input_name (type_decl : Ppxlib.type_declaration
370407
; ptype_name
371408
; ptype_manifest =
372409
Some ({ptyp_desc = Ptyp_constr ({txt = lid; loc}, _); _} as manifest)
373-
; _ } ->
374-
if tool_name = "ocamldep" then
375-
(* Just put it as manifest *)
376-
if is_self_reference ~input_name lid then
377-
{type_decl with ptype_manifest = None}
378-
else {type_decl with ptype_manifest = Some manifest}
379-
else
380-
Ast_helper.with_default_loc loc (fun () ->
381-
let ttype_decl =
382-
let env = Lazy.force lazy_env in
383-
match lid with
384-
| Lapply _ ->
385-
Location.raise_errorf ~loc
386-
"[%%import] cannot import a functor application %s"
387-
(string_of_lid lid)
388-
| Lident _ as head_id ->
389-
(* In this case, we know for sure that the user intends this lident
390-
as a type name, so we use Typetexp.find_type and let the failure
391-
cases propagate to the user. *)
392-
Compat.find_type env ~loc head_id |> snd
393-
| Ldot (parent_id, elem) ->
394-
let sig_items = locate_sig ~loc env parent_id in
395-
get_type_decl ~loc sig_items parent_id elem
396-
in
397-
let m, s =
398-
if is_self_reference ~input_name lid then (None, [])
399-
else
400-
let subst = subst_of_manifest manifest in
401-
let subst =
402-
subst
403-
@ [ ( `Lid (Lident (Longident.last_exn lid))
404-
, Ast_helper.Typ.constr
405-
{txt = Lident ptype_name.txt; loc = ptype_name.loc}
406-
[] ) ]
407-
in
408-
(Some manifest, subst)
409-
in
410-
let ptype_decl =
411-
ptype_decl_of_ttype_decl ~manifest:m ~subst:s ptype_name ttype_decl
412-
in
413-
{ptype_decl with ptype_attributes} )
410+
; _ } -> (
411+
try
412+
if tool_name = "ocamldep" then
413+
(* Just put it as manifest *)
414+
if is_self_reference ~input_name ~loc lid then
415+
{type_decl with ptype_manifest = None}
416+
else {type_decl with ptype_manifest = Some manifest}
417+
else
418+
Ast_helper.with_default_loc loc (fun () ->
419+
let ttype_decl =
420+
let env = Lazy.force lazy_env in
421+
match lid with
422+
| Lapply _ ->
423+
let error =
424+
Printf.sprintf
425+
"[%%import] cannot import a functor application %s"
426+
(string_of_lid lid)
427+
in
428+
raise_error ~loc error
429+
| Lident _ as head_id ->
430+
(* In this case, we know for sure that the user intends this lident
431+
as a type name, so we use Typetexp.find_type and let the failure
432+
cases propagate to the user. *)
433+
Compat.find_type env ~loc head_id |> snd
434+
| Ldot (parent_id, elem) ->
435+
let sig_items = locate_sig ~loc env parent_id in
436+
get_type_decl ~loc sig_items parent_id elem
437+
in
438+
let m, s =
439+
if is_self_reference ~input_name ~loc lid then (None, [])
440+
else
441+
let subst = subst_of_manifest manifest in
442+
let subst =
443+
subst
444+
@ [ ( `Lid (Lident (Longident.last_exn lid))
445+
, Ast_helper.Typ.constr
446+
{txt = Lident ptype_name.txt; loc = ptype_name.loc}
447+
[] ) ]
448+
in
449+
(Some manifest, subst)
450+
in
451+
let ptype_decl =
452+
ptype_decl_of_ttype_decl ~manifest:m ~subst:s ptype_name
453+
ttype_decl
454+
in
455+
{ptype_decl with ptype_attributes} )
456+
with Error {loc; error} ->
457+
let ext = Ppxlib.Location.error_extensionf ~loc "%s" error in
458+
let core_type = Ast_builder.Default.ptyp_extension ~loc ext in
459+
{type_decl with ptype_manifest = Some core_type} )
414460
| _ -> type_decl
415461

416462
let rec cut_tsig_block_of_rec_types accu (tsig : Compat.signature_item_407 list)
@@ -464,42 +510,60 @@ let rec psig_of_tsig ~subst (tsig : Compat.signature_item_407 list) :
464510

465511
let module_type ~tool_name ~input_name (package_type : Ppxlib.package_type) =
466512
let open Ppxlib in
467-
let ({txt = lid; loc} as alias), subst = package_type in
468-
if tool_name = "ocamldep" then
469-
if is_self_reference ~input_name lid then
470-
(* Create a dummy module type to break the circular dependency *)
471-
Ast_helper.Mty.mk ~attrs:[] (Pmty_signature [])
472-
else (* Just put it as alias *)
473-
Ast_helper.Mty.mk ~attrs:[] (Pmty_alias alias)
474-
else
475-
Ppxlib.Ast_helper.with_default_loc loc (fun () ->
476-
let env = Lazy.force lazy_env in
477-
let tmodtype_decl =
478-
match lid with
479-
| Longident.Lapply _ ->
480-
Location.raise_errorf ~loc
481-
"[%%import] cannot import a functor application %s"
482-
(string_of_lid lid)
483-
| Longident.Lident _ as head_id ->
484-
(* In this case, we know for sure that the user intends this lident
485-
as a module type name, so we use Typetexp.find_type and
486-
let the failure cases propagate to the user. *)
487-
Compat.find_modtype env ~loc head_id |> snd
488-
| Longident.Ldot (parent_id, elem) ->
489-
let sig_items = locate_sig ~loc env parent_id in
490-
get_modtype_decl ~loc sig_items parent_id elem
491-
in
492-
match tmodtype_decl with
493-
| {mtd_type = Some (Mty_signature tsig); _} ->
494-
let subst = List.map (fun ({txt; _}, typ) -> (`Lid txt, typ)) subst in
495-
let psig =
496-
psig_of_tsig ~subst (List.map Compat.migrate_signature_item tsig)
513+
try
514+
let ({txt = lid; loc} as alias), subst = package_type in
515+
if tool_name = "ocamldep" then
516+
if is_self_reference ~input_name ~loc lid then
517+
(* Create a dummy module type to break the circular dependency *)
518+
Ast_helper.Mty.mk ~attrs:[] (Pmty_signature [])
519+
else
520+
(* Just put it as alias *)
521+
Ast_helper.Mty.mk ~attrs:[] (Pmty_alias alias)
522+
else
523+
Ppxlib.Ast_helper.with_default_loc loc (fun () ->
524+
let env = Lazy.force lazy_env in
525+
let tmodtype_decl =
526+
match lid with
527+
| Longident.Lapply _ ->
528+
let error =
529+
Printf.sprintf
530+
"[%%import] cannot import a functor application %s"
531+
(string_of_lid lid)
532+
in
533+
raise_error ~loc error
534+
| Longident.Lident _ as head_id ->
535+
(* In this case, we know for sure that the user intends this lident
536+
as a module type name, so we use Typetexp.find_type and
537+
let the failure cases propagate to the user. *)
538+
Compat.find_modtype env ~loc head_id |> snd
539+
| Longident.Ldot (parent_id, elem) ->
540+
let sig_items = locate_sig ~loc env parent_id in
541+
get_modtype_decl ~loc sig_items parent_id elem
497542
in
498-
Ast_helper.Mty.mk ~attrs:[] (Pmty_signature psig)
499-
| {mtd_type = None; _} ->
500-
Location.raise_errorf ~loc "Imported module is abstract"
501-
| _ ->
502-
Location.raise_errorf ~loc "Imported module is indirectly defined" )
543+
match tmodtype_decl with
544+
| {mtd_type = Some (Mty_signature tsig); _} ->
545+
let subst =
546+
List.map (fun ({txt; _}, typ) -> (`Lid txt, typ)) subst
547+
in
548+
let psig =
549+
psig_of_tsig ~subst (List.map Compat.migrate_signature_item tsig)
550+
in
551+
Ast_helper.Mty.mk ~attrs:[] (Pmty_signature psig)
552+
| {mtd_type = None; _} ->
553+
let ext =
554+
Ppxlib.Location.error_extensionf ~loc
555+
"Imported module is abstract"
556+
in
557+
Ast_builder.Default.pmty_extension ~loc ext
558+
| _ ->
559+
let ext =
560+
Ppxlib.Location.error_extensionf ~loc
561+
"Imported module is indirectly defined"
562+
in
563+
Ast_builder.Default.pmty_extension ~loc ext )
564+
with Error {loc; error} ->
565+
let ext = Ppxlib.Location.error_extensionf ~loc "%s" error in
566+
Ast_builder.Default.pmty_extension ~loc ext
503567

504568
let type_declaration_expand ~ctxt rec_flag type_decls =
505569
let loc = Ppxlib.Expansion_context.Extension.extension_point_loc ctxt in

src_test/ppx_deriving/errors/run.t

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ Functor error
1313
> [%%import: type t = Map.Make(String).t]
1414
> EOF
1515

16-
$ dune build 2>&1 | sed 's/\(Command line: \).*/\1Error/'
17-
File ".test.objs/byte/_unknown_", line 1, characters 0-0:
18-
Fatal error: exception Invalid_argument("Ppxlib.Longident.flatten")
19-
File "test.ml", line 1:
20-
Error: Error while running external preprocessor
21-
Command line: Error
22-
16+
$ dune build
17+
File "test.ml", line 1, characters 21-39:
18+
1 | [%%import: type t = Map.Make(String).t]
19+
^^^^^^^^^^^^^^^^^^
20+
Error: [%import] cannot import a functor application Map.Make(String)
21+
[1]
22+
2323
Parameters error
2424
$ cat >test.ml <<EOF
2525
> [%%import: type t = List.t]
@@ -96,6 +96,8 @@ Cannot find module error
9696
> EOF
9797

9898
$ dune build
99-
File "_none_", line 1:
99+
File "test.ml", line 1, characters 34-43:
100+
1 | module type A = [%import: (module Stuff.S.M)]
101+
^^^^^^^^^
100102
Error: [%import]: cannot find the module type M in Stuff.S
101103
[1]

0 commit comments

Comments
 (0)