diff --git a/compiler/bin-js_of_ocaml/cmd_arg.ml b/compiler/bin-js_of_ocaml/cmd_arg.ml index ac0b2af78c..13367e20f0 100644 --- a/compiler/bin-js_of_ocaml/cmd_arg.ml +++ b/compiler/bin-js_of_ocaml/cmd_arg.ml @@ -80,6 +80,26 @@ type t = ; effects : Config.effects_backend } +let set_param = + let doc = "Set compiler options." in + let all = List.map (Config.Param.all ()) ~f:(fun (x, _, _) -> x, x) in + let pair = Arg.(pair ~sep:'=' (enum all) string) in + let parser s = + match Arg.conv_parser pair s with + | Ok (k, v) -> ( + match + List.find ~f:(fun (k', _, _) -> String.equal k k') (Config.Param.all ()) + with + | _, _, valid -> ( + match valid v with + | Ok () -> Ok (k, v) + | Error msg -> Error (`Msg ("Unexpected VALUE after [=], " ^ msg)))) + | Error _ as e -> e + in + let printer = Arg.conv_printer pair in + let c = Arg.conv (parser, printer) in + Arg.(value & opt_all (list c) [] & info [ "set" ] ~docv:"PARAM=VALUE" ~doc) + let wrap_with_fun_conv = let conv s = if String.equal s "" @@ -180,14 +200,6 @@ let options = in Arg.(value & opt wrap_with_fun_conv `Iife & info [ "wrap-with-fun" ] ~doc) in - let set_param = - let doc = "Set compiler options." in - let all = List.map (Config.Param.all ()) ~f:(fun (x, _) -> x, x) in - Arg.( - value - & opt_all (list (pair ~sep:'=' (enum all) string)) [] - & info [ "set" ] ~docv:"PARAM=VALUE" ~doc) - in let set_env = let doc = "Set environment variable statically." in Arg.( @@ -502,14 +514,6 @@ let options_runtime_only = in Arg.(value & opt wrap_with_fun_conv `Iife & info [ "wrap-with-fun" ] ~doc) in - let set_param = - let doc = "Set compiler options." in - let all = List.map (Config.Param.all ()) ~f:(fun (x, _) -> x, x) in - Arg.( - value - & opt_all (list (pair ~sep:'=' (enum all) string)) [] - & info [ "set" ] ~docv:"PARAM=VALUE" ~doc) - in let set_env = let doc = "Set environment variable statically." in Arg.( diff --git a/compiler/bin-js_of_ocaml/compile.ml b/compiler/bin-js_of_ocaml/compile.ml index 6364f88776..deb995b991 100644 --- a/compiler/bin-js_of_ocaml/compile.ml +++ b/compiler/bin-js_of_ocaml/compile.ml @@ -226,10 +226,11 @@ let run let check_debug (one : Parse_bytecode.one) = if Option.is_some source_map && Parse_bytecode.Debug.is_empty one.debug then - warn - "Warning: '--source-map' is enabled but the bytecode program was compiled with \ - no debugging information.\n\ - Warning: Consider passing '-g' option to ocamlc.\n\ + Warning.warn + `Missing_debug_event + "'--source-map' is enabled but the bytecode program was compiled with no \ + debugging information.\n\ + Consider passing '-g' option to ocamlc.\n\ %!" in let pseudo_fs_instr prim debug cmis = diff --git a/compiler/bin-js_of_ocaml/js_of_ocaml.ml b/compiler/bin-js_of_ocaml/js_of_ocaml.ml index 7d665d927a..f4203025e8 100644 --- a/compiler/bin-js_of_ocaml/js_of_ocaml.ml +++ b/compiler/bin-js_of_ocaml/js_of_ocaml.ml @@ -23,7 +23,7 @@ open Js_of_ocaml_compiler let () = Sys.catch_break true; - let argv = Jsoo_cmdline.normalize_argv ~warn:(warn "%s") Sys.argv in + let argv = Sys.argv in let argv = let like_arg x = String.length x > 0 && Char.equal x.[0] '-' in let like_command x = @@ -59,11 +59,8 @@ let () = ]) with | Ok (`Ok () | `Help | `Version) -> - if !warnings > 0 && !werror - then ( - Format.eprintf "%s: all warnings being treated as errors@." Sys.argv.(0); - exit 1) - else exit 0 + Warning.process_warnings (); + exit 0 | Error `Term -> exit 1 | Error `Parse -> exit Cmdliner.Cmd.Exit.cli_error | Error `Exn -> () diff --git a/compiler/bin-jsoo_minify/jsoo_minify.ml b/compiler/bin-jsoo_minify/jsoo_minify.ml index f1640187fd..f636ab50e0 100644 --- a/compiler/bin-jsoo_minify/jsoo_minify.ml +++ b/compiler/bin-jsoo_minify/jsoo_minify.ml @@ -92,12 +92,7 @@ let main = Cmdliner.Cmd.v Cmd_arg.info t let (_ : int) = - try - Cmdliner.Cmd.eval - ~catch:false - ~argv:(Jsoo_cmdline.normalize_argv ~warn:(warn "%s") Sys.argv) - main - with + try Cmdliner.Cmd.eval ~catch:false ~argv:Sys.argv main with | (Match_failure _ | Assert_failure _ | Not_found) as exc -> let backtrace = Printexc.get_backtrace () in Format.eprintf diff --git a/compiler/bin-wasm_of_ocaml/cmd_arg.ml b/compiler/bin-wasm_of_ocaml/cmd_arg.ml index 1dcce5b33d..5911580f46 100644 --- a/compiler/bin-wasm_of_ocaml/cmd_arg.ml +++ b/compiler/bin-wasm_of_ocaml/cmd_arg.ml @@ -66,6 +66,26 @@ type t = ; shape_files : string list } +let set_param = + let doc = "Set compiler options." in + let all = List.map (Config.Param.all ()) ~f:(fun (x, _, _) -> x, x) in + let pair = Arg.(pair ~sep:'=' (enum all) string) in + let parser s = + match Arg.conv_parser pair s with + | Ok (k, v) -> ( + match + List.find ~f:(fun (k', _, _) -> String.equal k k') (Config.Param.all ()) + with + | _, _, valid -> ( + match valid v with + | Ok () -> Ok (k, v) + | Error msg -> Error (`Msg ("Unexpected VALUE after [=], " ^ msg)))) + | Error _ as e -> e + in + let printer = Arg.conv_printer pair in + let c = Arg.conv (parser, printer) in + Arg.(value & opt_all (list c) [] & info [ "set" ] ~docv:"PARAM=VALUE" ~doc) + let options () = let runtime_files = let doc = "Link JavaScript and WebAssembly files [$(docv)]. " in @@ -110,14 +130,6 @@ let options () = let doc = "root dir for source map." in Arg.(value & opt (some string) None & info [ "source-map-root" ] ~doc) in - let set_param = - let doc = "Set compiler options." in - let all = List.map (Config.Param.all ()) ~f:(fun (x, _) -> x, x) in - Arg.( - value - & opt_all (list (pair ~sep:'=' (enum all) string)) [] - & info [ "set" ] ~docv:"PARAM=VALUE" ~doc) - in let include_dirs = let doc = "Add [$(docv)] to the list of include directories." in Arg.(value & opt_all string [] & info [ "I" ] ~docv:"DIR" ~doc) @@ -232,14 +244,6 @@ let options_runtime_only () = let doc = "Add [$(docv)] to the list of include directories." in Arg.(value & opt_all string [] & info [ "I" ] ~docv:"DIR" ~doc) in - let set_param = - let doc = "Set compiler options." in - let all = List.map (Config.Param.all ()) ~f:(fun (x, _) -> x, x) in - Arg.( - value - & opt_all (list (pair ~sep:'=' (enum all) string)) [] - & info [ "set" ] ~docv:"PARAM=VALUE" ~doc) - in let effects = let doc = "Select an implementation of effect handlers. [$(docv)] should be one of $(b,jspi) \ diff --git a/compiler/bin-wasm_of_ocaml/compile.ml b/compiler/bin-wasm_of_ocaml/compile.ml index 9a11c79802..08fcfda78d 100644 --- a/compiler/bin-wasm_of_ocaml/compile.ml +++ b/compiler/bin-wasm_of_ocaml/compile.ml @@ -414,9 +414,10 @@ let run && Parse_bytecode.Debug.is_empty one.debug && not (Code.is_empty one.code) then - warn - "Warning: '--source-map' is enabled but the bytecode program was compiled with \ - no debugging information.\n\ + Warning.warn + `Missing_debug_event + "'--source-map' is enabled but the bytecode program was compiled with no \ + debugging information.\n\ Warning: Consider passing '-g' option to ocamlc.\n\ %!" in diff --git a/compiler/bin-wasm_of_ocaml/gen/gen.ml b/compiler/bin-wasm_of_ocaml/gen/gen.ml index 0310943921..2083f81cfd 100644 --- a/compiler/bin-wasm_of_ocaml/gen/gen.ml +++ b/compiler/bin-wasm_of_ocaml/gen/gen.ml @@ -17,7 +17,7 @@ let check_js_file fname = let freenames = StringSet.diff freenames Reserved.provided in if not (StringSet.is_empty freenames) then ( - Format.eprintf "warning: free variables in %S@." fname; + Format.eprintf "Warning: free variables in %S@." fname; Format.eprintf "vars: %s@." (String.concat ~sep:", " (StringSet.elements freenames)); exit 2); () diff --git a/compiler/bin-wasm_of_ocaml/wasm_of_ocaml.ml b/compiler/bin-wasm_of_ocaml/wasm_of_ocaml.ml index 91fe026df7..bc87e9ba75 100644 --- a/compiler/bin-wasm_of_ocaml/wasm_of_ocaml.ml +++ b/compiler/bin-wasm_of_ocaml/wasm_of_ocaml.ml @@ -21,7 +21,7 @@ open Js_of_ocaml_compiler let () = Sys.catch_break true; - let argv = Jsoo_cmdline.normalize_argv ~warn:(warn "%s") Sys.argv in + let argv = Sys.argv in let argv = let like_arg x = String.length x > 0 && Char.equal x.[0] '-' in let like_command x = @@ -57,11 +57,8 @@ let () = ]) with | Ok (`Ok () | `Help | `Version) -> - if !warnings > 0 && !werror - then ( - Format.eprintf "%s: all warnings being treated as errors@." Sys.argv.(0); - exit 1) - else exit 0 + Warning.process_warnings (); + exit 0 | Error `Term -> exit 1 | Error `Parse -> exit Cmdliner.Cmd.Exit.cli_error | Error `Exn -> () diff --git a/compiler/bin-wasm_of_ocaml/wasmoo_link_wasm.ml b/compiler/bin-wasm_of_ocaml/wasmoo_link_wasm.ml index d9f7a24766..1192fbe17d 100644 --- a/compiler/bin-wasm_of_ocaml/wasmoo_link_wasm.ml +++ b/compiler/bin-wasm_of_ocaml/wasmoo_link_wasm.ml @@ -20,12 +20,7 @@ open Js_of_ocaml_compiler.Stdlib let (_ : int) = - try - Cmdliner.Cmd.eval - ~catch:false - ~argv:(Jsoo_cmdline.normalize_argv ~warn:(warn "%s") Sys.argv) - Link_wasm.command - with + try Cmdliner.Cmd.eval ~catch:false ~argv:Sys.argv Link_wasm.command with | (Match_failure _ | Assert_failure _ | Not_found) as exc -> let backtrace = Printexc.get_backtrace () in Format.eprintf diff --git a/compiler/lib-cmdline/arg.ml b/compiler/lib-cmdline/arg.ml index 2b30270cdf..bceec63034 100644 --- a/compiler/lib-cmdline/arg.ml +++ b/compiler/lib-cmdline/arg.ml @@ -31,6 +31,7 @@ type t = ; optim : string list on_off ; quiet : bool ; werror : bool + ; warnings : (bool * Warning.t) list ; custom_header : string option } @@ -61,6 +62,35 @@ let disable = in Term.(const List.flatten $ arg)) +let parse_warning s = + let err s = `Msg (Printf.sprintf "Unknown warning %s" s) in + if String.is_empty s + then Error (err s) + else + match Warning.parse s with + | Some n -> Ok (true, n) + | None -> ( + match String.drop_prefix ~prefix:"no-" s with + | Some n -> ( + match Warning.parse n with + | Some n -> Ok (false, n) + | None -> Error (err n)) + | None -> Error (err s)) + +let print_warning fmt (b, w) = + Format.fprintf + fmt + "%s%s" + (match b with + | true -> "" + | false -> "") + (Warning.name w) + +let warnings : (bool * Warning.t) list Term.t = + let doc = "Enable or disable the warnings specified by the argument [$(docv)]." in + let c : 'a Arg.conv = Arg.conv ~docv:"" (parse_warning, print_warning) in + Arg.(value & opt_all c [] & info [ "w" ] ~docv:"WARN" ~doc) + let pretty = let doc = "Pretty print the output." in Arg.(value & flag & info [ "pretty" ] ~doc) @@ -91,7 +121,19 @@ let custom_header = let t = lazy Term.( - const (fun debug enable disable pretty debuginfo noinline quiet werror c_header -> + const + (fun + debug + enable + disable + pretty + debuginfo + noinline + quiet + (warnings : (bool * Warning.t) list) + werror + c_header + -> let enable = if pretty then "pretty" :: enable else enable in let enable = if debuginfo then "debuginfo" :: enable else enable in let disable = if noinline then "inline" :: disable else disable in @@ -104,6 +146,7 @@ let t = let disable = disable_if_pretty "share" disable in { debug = { enable = debug; disable = [] } ; optim = { enable; disable } + ; warnings ; quiet ; werror ; custom_header = c_header @@ -115,6 +158,7 @@ let t = $ debuginfo $ noinline $ is_quiet + $ warnings $ is_werror $ custom_header) @@ -125,5 +169,8 @@ let on_off on off t = let eval t = Config.Flag.(on_off enable disable t.optim); Debug.(on_off enable disable t.debug); - quiet := t.quiet; - werror := t.werror + List.iter t.warnings ~f:(function + | true, w -> Warning.enable w + | false, w -> Warning.disable w); + Warning.quiet := t.quiet; + Warning.werror := t.werror diff --git a/compiler/lib-cmdline/arg.mli b/compiler/lib-cmdline/arg.mli index 295f58ac72..16a0eb6841 100644 --- a/compiler/lib-cmdline/arg.mli +++ b/compiler/lib-cmdline/arg.mli @@ -27,6 +27,7 @@ type t = ; optim : string list on_off ; quiet : bool ; werror : bool + ; warnings : (bool * Js_of_ocaml_compiler.Warning.t) list ; custom_header : string option } diff --git a/compiler/lib-cmdline/jsoo_cmdline.ml b/compiler/lib-cmdline/jsoo_cmdline.ml index 9943936bc7..2acd3ddd18 100644 --- a/compiler/lib-cmdline/jsoo_cmdline.ml +++ b/compiler/lib-cmdline/jsoo_cmdline.ml @@ -17,33 +17,4 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) -open Js_of_ocaml_compiler.Stdlib module Arg = Arg - -let normalize_argv ?(warn = fun _ -> ()) a = - let bad = ref [] in - let a = - Array.map - ~f:(fun s -> - let size = String.length s in - if size <= 2 - then s - else if - Char.equal s.[0] '-' - && (not (Char.equal s.[1] '-')) - && not (Char.equal s.[2] '=') - then ( - bad := s :: !bad; - (* long option with one dash lets double the dash *) - "-" ^ s) - else s) - a - in - if not (List.is_empty !bad) - then - warn - (Format.sprintf - "[Warning] long options with a single '-' are now deprecated. Please use '--' \ - for the following options: %s@." - (String.concat ~sep:", " !bad)); - a diff --git a/compiler/lib-wasm/generate.ml b/compiler/lib-wasm/generate.ml index a1dd5a8b26..7db9e077bb 100644 --- a/compiler/lib-wasm/generate.ml +++ b/compiler/lib-wasm/generate.ml @@ -1397,7 +1397,6 @@ module Generate (Target : Target_sig.S) = struct ~live_vars ~in_cps (* ~should_export - ~warn_on_unhandled_effect *) ~deadcode_sentinal ~global_flow_info diff --git a/compiler/lib-wasm/link.ml b/compiler/lib-wasm/link.ml index f2c527323b..3f905507f1 100644 --- a/compiler/lib-wasm/link.ml +++ b/compiler/lib-wasm/link.ml @@ -409,12 +409,15 @@ let output_js js = let report_missing_primitives missing = if not (List.is_empty missing) - then ( - warn "There are some missing Wasm primitives@."; - warn "Dummy implementations (raising an exception) "; - warn "will be provided.@."; - warn "Missing primitives:@."; - List.iter ~f:(fun nm -> warn " %s@." nm) missing) + then + Warning.warn + `Missing_primitive + "There are some missing Wasm primitives\n\ + Dummy implementations (raising an exception) will be provided.\n\ + Missing primitives:\n\ + %a" + (Format.pp_print_list Format.pp_print_string) + missing let build_runtime_arguments ~link_spec diff --git a/compiler/lib/builtins.ml b/compiler/lib/builtins.ml index 1319030375..91b244d080 100644 --- a/compiler/lib/builtins.ml +++ b/compiler/lib/builtins.ml @@ -40,7 +40,9 @@ let register ~name ~content ~fragments = let name = "+" ^ name in let t = { File.name; content; fragments } in if String.Hashtbl.mem tbl name - then warn "The builtin runtime file %S was registered multiple time" name; + then + failwith + (Printf.sprintf "The builtin runtime file %S was registered multiple time" name); String.Hashtbl.add tbl name t; t diff --git a/compiler/lib/config.ml b/compiler/lib/config.ml index c5cab8ffe7..65c45d39ca 100644 --- a/compiler/lib/config.ml +++ b/compiler/lib/config.ml @@ -89,8 +89,6 @@ module Flag = struct let improved_stacktrace = o ~name:"with-js-error" ~default:false - let warn_unused = o ~name:"warn-unused" ~default:false - let inline_callgen = o ~name:"callgen" ~default:false let safe_string = o ~name:"safestring" ~default:true @@ -111,36 +109,51 @@ module Flag = struct end module Param = struct - let int default = default, int_of_string + let int default = + ( default + , int_of_string + , fun s -> + try + ignore (int_of_string s : int); + Ok () + with _ -> Error "expecting an integer" ) let enum : (string * 'a) list -> _ = function - | (_, v) :: _ as l -> ( + | (_, v) :: _ as l -> ( v - , fun x -> + , (fun x -> match List.string_assoc x l with | Some x -> x - | None -> assert false )) + | None -> assert false) + , fun x -> + if List.exists ~f:(fun (y, _) -> String.equal x y) l + then Ok () + else + Error + (Printf.sprintf + "expecting one of %s" + (String.concat ~sep:", " (List.map l ~f:fst))) ) | _ -> assert false let params : (string * _) list ref = ref [] - let p ~name ~desc (default, convert) = + let p ~name ~desc (default, convert, valid) = assert (Option.is_none (List.string_assoc name !params)); let state = ref default in let set : string -> unit = fun v -> try state := convert v - with _ -> warn "Warning: malformed option %s=%s. IGNORE@." name v + with _ -> failwith (Printf.sprintf "malformed option %s=%s." name v) in - params := (name, (set, desc)) :: !params; + params := (name, (set, desc, valid)) :: !params; fun () -> !state let set s v = match List.string_assoc s !params with - | Some (f, _) -> f v + | Some (f, _, _) -> f v | None -> failwith (Printf.sprintf "The option named %S doesn't exist" s) - let all () = List.map !params ~f:(fun (n, (_, d)) -> n, d) + let all () = List.map !params ~f:(fun (n, (_, d, valid)) -> n, d, valid) (* V8 "optimize" switches with less than 128 case. 60 seams to perform well. *) diff --git a/compiler/lib/config.mli b/compiler/lib/config.mli index 09c8b6ab1a..fc545a3fc4 100644 --- a/compiler/lib/config.mli +++ b/compiler/lib/config.mli @@ -60,8 +60,6 @@ module Flag : sig val improved_stacktrace : unit -> bool - val warn_unused : unit -> bool - val inline_callgen : unit -> bool val safe_string : unit -> bool @@ -87,7 +85,7 @@ end module Param : sig val set : string -> string -> unit - val all : unit -> (string * string) list + val all : unit -> (string * string * (string -> (unit, string) Result.t)) list val switch_max_case : unit -> int diff --git a/compiler/lib/debug.ml b/compiler/lib/debug.ml index 248eedab56..20f03f40fd 100644 --- a/compiler/lib/debug.ml +++ b/compiler/lib/debug.ml @@ -57,7 +57,7 @@ let find ?(even_if_quiet = false) s = in fun () -> if String.equal s "times" then take_snapshot (); - (even_if_quiet || not !quiet) && !state + (even_if_quiet || not !Warning.quiet) && !state let enable s = match List.string_assoc s !debugs with diff --git a/compiler/lib/driver.ml b/compiler/lib/driver.ml index b5bb3c1211..b5cd1aa938 100644 --- a/compiler/lib/driver.ml +++ b/compiler/lib/driver.ml @@ -267,7 +267,7 @@ let extra_js_files = (name, ss) :: acc with _ -> acc)) -let report_missing_primitives missing = +let report_missing_primitives fmt missing = let missing = List.fold_left (Lazy.force extra_js_files) @@ -276,15 +276,15 @@ let report_missing_primitives missing = let d = StringSet.inter missing pro in if not (StringSet.is_empty d) then ( - warn "Missing primitives provided by %s:@." file; - StringSet.iter (fun nm -> warn " %s@." nm) d; + Format.fprintf fmt "Missing primitives provided by %s:@." file; + StringSet.iter (fun nm -> Format.fprintf fmt " %s@." nm) d; StringSet.diff missing pro) else missing) in if not (StringSet.is_empty missing) then ( - warn "Missing primitives:@."; - StringSet.iter (fun nm -> warn " %s@." nm) missing) + Format.fprintf fmt "Missing primitives:@."; + StringSet.iter (fun nm -> Format.fprintf fmt " %s@." nm) missing) let gen_missing js missing = let open Javascript in @@ -324,13 +324,17 @@ let gen_missing js missing = [] in if not (StringSet.is_empty missing) - then ( - warn "There are some missing primitives@."; - warn "Dummy implementations (raising 'Failure' exception) "; - warn "will be used if they are not available at runtime.@."; - warn "You can prevent the generation of dummy implementations with "; - warn "the commandline option '--disable genprim'@."; - report_missing_primitives missing); + then + Warning.warn + `Missing_primitive + "There are some missing primitives.\n\ + Dummy implementations (raising 'Failure' exception) will be used if they are not \ + available at runtime.\n\ + You can prevent the generation of dummy implementations with the commandline \ + option '--disable genprim'\n\ + %a" + report_missing_primitives + missing; (variable_declaration miss, N) :: js let mark_start_of_generated_code = Debug.find ~even_if_quiet:true "mark-runtime-gen" @@ -356,18 +360,25 @@ let link' ~export_runtime ~standalone ~link (js : Javascript.statement_list) : in let used = let all_provided = Linker.list_all () in + let free = + lazy + (let free = ref StringSet.empty in + let o = + new Js_traverse.fast_freevar (fun s -> free := StringSet.add s !free) + in + o#program js; + !free) + in match link with - | `All -> all_provided + | `All -> + let prim = Primitive.get_external () in + StringSet.union (StringSet.inter prim (Lazy.force free)) all_provided | `All_from from -> Linker.list_all ~from () | `No -> StringSet.empty | `Needed -> - let free = ref StringSet.empty in - let o = new Js_traverse.fast_freevar (fun s -> free := StringSet.add s !free) in - o#program js; - let free = !free in let prim = Primitive.get_external () in let all_external = StringSet.union prim all_provided in - StringSet.inter free all_external + StringSet.inter (Lazy.force free) all_external in let linkinfos = let from = @@ -473,17 +484,23 @@ let check_js js = let missing = StringSet.inter free all_external in let missing = StringSet.diff missing Reserved.provided in let other = StringSet.diff free missing in - if not (StringSet.is_empty missing) then report_missing_primitives missing; + if not (StringSet.is_empty missing) + then + Warning.warn + `Missing_primitive + "There are some missing primitives.\n%a" + report_missing_primitives + missing; let probably_prov = StringSet.inter other Reserved.provided in let other = StringSet.diff other probably_prov in if (not (StringSet.is_empty other)) && debug_linker () then ( - warn "Missing variables:@."; - StringSet.iter (fun nm -> warn " %s@." nm) other); + Format.eprintf "Missing variables:@."; + StringSet.iter (fun nm -> Format.eprintf " %s@." nm) other); if (not (StringSet.is_empty probably_prov)) && debug_linker () then ( - warn "Variables provided by the browser:@."; - StringSet.iter (fun nm -> warn " %s@." nm) probably_prov); + Format.eprintf "Variables provided by the browser:@."; + StringSet.iter (fun nm -> Format.eprintf " %s@." nm) probably_prov); if times () then Format.eprintf " checks: %a@." Timer.print t; js diff --git a/compiler/lib/generate.ml b/compiler/lib/generate.ml index 6a6f729c72..4647e27d3f 100644 --- a/compiler/lib/generate.ml +++ b/compiler/lib/generate.ml @@ -1522,8 +1522,9 @@ let rec translate_expr ctx loc x e level : (_ * J.statement_list) Expr_builder.t assert (not (cps_transform ())); if not !(ctx.effect_warning) then ( - warn - "Warning: your program contains effect handlers; you should probably run \ + Warning.warn + `Effect_handlers_without_effect_backend + "your program contains effect handlers; you should probably run \ js_of_ocaml with option '--effects=cps'@."; ctx.effect_warning := true); let name = "jsoo_effect_not_supported" in diff --git a/compiler/lib/link_js.ml b/compiler/lib/link_js.ml index e2ea0cc902..82ab0ce928 100644 --- a/compiler/lib/link_js.ml +++ b/compiler/lib/link_js.ml @@ -345,9 +345,10 @@ let link if u.effects_without_cps && not !warn_effects then ( warn_effects := true; - warn - "Warning: your program contains effect handlers; you should \ - probably run js_of_ocaml with option '--effects=cps'@."); + Warning.warn + `Effect_handlers_without_effect_backend + "your program contains effect handlers; you should probably run \ + js_of_ocaml with option '--effects=cps'@."); (if mklib then let u = if linkall then { u with force_link = true } else u in diff --git a/compiler/lib/linker.ml b/compiler/lib/linker.ml index f0985384bf..f66b511c13 100644 --- a/compiler/lib/linker.ml +++ b/compiler/lib/linker.ml @@ -112,8 +112,9 @@ module Check = struct (match diff with | [] -> () | l -> - warn - "WARN unused for primitive %s at %s:@. %s@." + Warning.warn + `Unused_js_variable + "unused variable for primitive %s at %s:@. %s@." name (loc pi) (String.concat ~sep:", " l)); @@ -122,7 +123,7 @@ module Check = struct let primitive ~name pi ~code ~requires ~has_flags = let freename = - if Config.Flag.warn_unused () + if Warning.enabled `Unused_js_variable then let o = new check_and_warn name pi in let _code = o#program code in @@ -147,21 +148,29 @@ module Check = struct in if StringSet.mem Global_constant.old_global_object freename then - warn - "warning: %s: 'joo_global_object' is being deprecated, please use `globalThis` \ - instead@." + Warning.warn + `Deprecated_joo_global_object + "%s: 'joo_global_object' is being deprecated, please use `globalThis` instead@." (loc pi); let freename = StringSet.remove Global_constant.old_global_object freename in if not (StringSet.mem name (Js_traverse.declared_names code)) then - warn - "warning: primitive code does not define value with the expected name: %s (%s)@." + Warning.warn + `Missing_define + "primitive code does not define value with the expected name: %s (%s)@." name (loc pi); if not (StringSet.is_empty freename) - then ( - warn "warning: free variables in primitive code %S (%s)@." name (loc pi); - warn "vars: %s@." (String.concat ~sep:", " (StringSet.elements freename))) + then + Warning.warn + `Free_variables_in_primitive + "free variables in primitive code %S (%s)@.vars: %a@." + name + (loc pi) + (Format.pp_print_list + ~pp_sep:(fun fmt () -> Format.pp_print_string fmt ", ") + Format.pp_print_string) + (StringSet.elements freename) end module Fragment = struct @@ -559,8 +568,9 @@ let load_fragment ~target_env ~filename (f : Fragment.t) = if p.weakdef then true else ( - warn - "warning: overriding primitive %S\n old: %s\n new: %s@." + Warning.warn + `Overriding_primitive + "overriding primitive %S\n old: %s\n new: %s@." name (loc p.pi) (loc pi); @@ -603,7 +613,8 @@ let check_deps () = then try let name, ploc = Int.Hashtbl.find provided_rev id in - warn + Warning.warn + `Missing_deps "code providing %s (%s) may miss dependencies: %s\n" name (loc ploc) @@ -732,7 +743,11 @@ let link ?(check_missing = true) program (state : state) = if false then let name = fst (Int.Hashtbl.find provided_rev x) in - warn "The runtime primitive [%s] is deprecated. %s\n" name txt + Warning.warn + `Deprecated_primitive + "The runtime primitive [%s] is deprecated. %s\n" + name + txt | x :: path -> let name = fst (Int.Hashtbl.find provided_rev x) in let path = @@ -742,7 +757,8 @@ let link ?(check_missing = true) program (state : state) = let nm, loc = Int.Hashtbl.find provided_rev id in Printf.sprintf "-> %s:%s" nm (Parse_info.to_string loc))) in - warn + Warning.warn + `Deprecated_primitive "The runtime primitive [%s] is deprecated. %s. Used by:\n%s\n" name txt diff --git a/compiler/lib/parse_bytecode.ml b/compiler/lib/parse_bytecode.ml index 56d870704e..4f089e7f61 100644 --- a/compiler/lib/parse_bytecode.ml +++ b/compiler/lib/parse_bytecode.ml @@ -2714,9 +2714,10 @@ let from_exe with Not_found -> if Debug.enabled debug_data || include_cmis then - warn - "Warning: Program not linked with -g, original variable names and locations \ - not available.@."); + Warning.warn + `Missing_debug_event + "Program not linked with -g, original variable names and locations not \ + available.@."); if times () then Format.eprintf " read debug events: %a@." Timer.print t; let globals = make_globals (Array.length init_data) init_data primitive_table in diff --git a/compiler/lib/primitive.ml b/compiler/lib/primitive.ml index 29ac474999..f8bd4bbebc 100644 --- a/compiler/lib/primitive.ml +++ b/compiler/lib/primitive.ml @@ -98,8 +98,9 @@ let register p k kargs arity = | exception Not_found -> () | k' when kind_equal k k' -> () | k' -> - warn - "Warning: overriding the purity of the primitive %s: %s -> %s@." + Warning.warn + `Overriding_primitive_purity + "overriding the purity of the primitive %s: %s -> %s@." p (string_of_kind k') (string_of_kind k)); diff --git a/compiler/lib/pseudo_fs.ml b/compiler/lib/pseudo_fs.ml index 4e5dfe7209..b9f5ac442f 100644 --- a/compiler/lib/pseudo_fs.ml +++ b/compiler/lib/pseudo_fs.ml @@ -27,19 +27,15 @@ let expand_path exts real virt = List.fold_left l ~init:acc ~f:(fun acc s -> loop (Filename.concat realfile s) (Filename.concat virtfile s) acc) else - try - let exmatch = - try - let b = Filename.basename realfile in - let i = String.rindex b '.' in - let e = String.sub b ~pos:(i + 1) ~len:(String.length b - i - 1) in - List.mem ~eq:String.equal e exts - with Not_found -> List.mem ~eq:String.equal "" exts - in - if List.is_empty exts || exmatch then (virtfile, realfile) :: acc else acc - with exc -> - warn "ignoring %s: %s@." realfile (Printexc.to_string exc); - acc + let exmatch = + try + let b = Filename.basename realfile in + let i = String.rindex b '.' in + let e = String.sub b ~pos:(i + 1) ~len:(String.length b - i - 1) in + List.mem ~eq:String.equal e exts + with Not_found -> List.mem ~eq:String.equal "" exts + in + if List.is_empty exts || exmatch then (virtfile, realfile) :: acc else acc in loop real virt [] @@ -124,11 +120,15 @@ let f ~prim ~cmis ~files ~paths = ([], []) in if not (List.is_empty missing_cmis) - then ( - warn "Some OCaml interface files were not found.@."; - warn "Use [-I dir_of_cmis] option to bring them into scope@."; - (* [`ocamlc -where`/expunge in.byte out.byte moduleA moduleB ... moduleN] *) - List.iter missing_cmis ~f:(fun nm -> warn " %s@." nm)); + then + Warning.warn + `Missing_cmi + "Some OCaml interface files were not found.\n\ + Use [-I dir_of_cmis] option to bring them into scope\n\ + %a" + (Format.pp_print_list Format.pp_print_string) + missing_cmis; + (* [`ocamlc -where`/expunge in.byte out.byte moduleA moduleB ... moduleN] *) let other_files = List.map files ~f:(fun f -> List.map (list_files f paths) ~f:(fun (name, filename) -> diff --git a/compiler/lib/stdlib.ml b/compiler/lib/stdlib.ml index bc2742ea93..8dfcfd63f1 100644 --- a/compiler/lib/stdlib.ml +++ b/compiler/lib/stdlib.ml @@ -70,19 +70,6 @@ let ( != ) = `use_phys_equal include Int_replace_polymorphic_compare -let quiet = ref false - -let werror = ref false - -let warnings = ref 0 - -let warn fmt = - Format.ksprintf - (fun s -> - incr warnings; - if not !quiet then Format.eprintf "%s%!" s) - fmt - let fail = ref true let failwith_ fmt = @@ -276,9 +263,9 @@ module Int32 = struct external ( >= ) : int32 -> int32 -> bool = "%greaterequal" let warn_overflow name ~to_dec ~to_hex i i32 = - warn - "Warning: integer overflow: %s 0x%s (%s) truncated to 0x%lx (%ld); the generated \ - code might be incorrect.@." + Warning.warn + `Integer_overflow + "%s 0x%s (%s) truncated to 0x%lx (%ld); the generated code might be incorrect.@." name (to_hex i) (to_dec i) diff --git a/compiler/lib/warning.ml b/compiler/lib/warning.ml new file mode 100644 index 0000000000..4e87834ebd --- /dev/null +++ b/compiler/lib/warning.ml @@ -0,0 +1,125 @@ +open StdLabels + +type t = + [ (* Parsing bytecode *) + `Integer_overflow + | `Missing_debug_event + | `Missing_cmi + | `Effect_handlers_without_effect_backend + | (* runtime *) + `Missing_primitive + | `Missing_define + | `Missing_deps + | `Deprecated_joo_global_object + | `Overriding_primitive + | `Overriding_primitive_purity + | `Deprecated_primitive + | `Unused_js_variable + | `Free_variables_in_primitive + ] + +module StringTable = Hashtbl.Make (struct + type t = string + + let equal = String.equal + + let hash = Hashtbl.hash +end) + +module Table = Hashtbl.Make (struct + type nonrec t = t + + let hash = Hashtbl.hash + + let equal (a : t) b = a = b +end) + +let state = Table.create 0 + +let enable t = Table.add state t true + +let disable t = Table.add state t false + +let default = function + (* Parsing bytecode *) + | `Integer_overflow | `Missing_debug_event | `Missing_cmi -> true + (* effects *) + | `Effect_handlers_without_effect_backend -> true + (* runtime *) + | `Missing_primitive | `Missing_define | `Missing_deps | `Free_variables_in_primitive -> + true + | `Deprecated_joo_global_object -> true + | `Overriding_primitive | `Overriding_primitive_purity -> true + | `Deprecated_primitive -> true + | `Unused_js_variable -> false + +let all = + [ (* Parsing bytecode *) + `Integer_overflow + ; `Missing_debug_event + ; `Missing_cmi + ; `Effect_handlers_without_effect_backend + ; (* runtime *) + `Missing_primitive + ; `Missing_define + ; `Missing_deps + ; `Deprecated_joo_global_object + ; `Overriding_primitive + ; `Overriding_primitive_purity + ; `Deprecated_primitive + ; `Unused_js_variable + ; `Free_variables_in_primitive + ] + +let name = function + (* Parsing bytecode *) + | `Integer_overflow -> "integer-overflow" + | `Missing_debug_event -> "missing-debug-event" + | `Missing_cmi -> "missing-cmi" + (* effects *) + | `Effect_handlers_without_effect_backend -> "missing-effects-backend" + (* runtime *) + | `Missing_primitive -> "missing-primitive" + | `Missing_define -> "missing-define" + | `Missing_deps -> "missing-deps" + | `Free_variables_in_primitive -> "free-variables" + | `Deprecated_joo_global_object -> "deprecated-joo-global-object" + | `Overriding_primitive -> "overriding-primitive" + | `Overriding_primitive_purity -> "overriding-primitive-purity" + | `Deprecated_primitive -> "deprecated-primitive" + | `Unused_js_variable -> "unused-js-vars" + +let parse : string -> t option = + let h = StringTable.create 18 in + List.iter all ~f:(fun t -> + let name = name t in + (* We use the no- prefix to disable warnings *) + assert (not (String.starts_with ~prefix:"no-" name)); + StringTable.add h name t); + fun s -> StringTable.find_opt h s + +let enabled t = + match Table.find_opt state t with + | Some b -> b + | None -> default t + +let quiet = ref false + +let werror = ref false + +let warnings = ref 0 + +let warn (t : t) fmt = + Format.kasprintf + (fun s -> + if enabled t && not !quiet + then ( + incr warnings; + Format.eprintf "Warning%s: %s%!" (Printf.sprintf " [%s]" (name t)) s)) + fmt + +let process_warnings () = + if !warnings > 0 && !werror + then ( + Format.eprintf "%s: all warnings being treated as errors@." Sys.argv.(0); + exit 1) diff --git a/compiler/lib/warning.mli b/compiler/lib/warning.mli new file mode 100644 index 0000000000..2b1eb2dc55 --- /dev/null +++ b/compiler/lib/warning.mli @@ -0,0 +1,37 @@ +type t = + [ (* Parsing bytecode *) + `Integer_overflow + | `Missing_debug_event + | `Missing_cmi + | `Effect_handlers_without_effect_backend + | (* runtime *) + `Missing_primitive + | `Missing_define + | `Missing_deps + | `Free_variables_in_primitive + | `Deprecated_joo_global_object + | `Overriding_primitive + | `Overriding_primitive_purity + | `Deprecated_primitive + | `Unused_js_variable + ] + +val all : t list + +val name : t -> string + +val parse : string -> t option + +val enable : t -> unit + +val disable : t -> unit + +val enabled : t -> bool + +val quiet : bool ref + +val werror : bool ref + +val warn : t -> ('a, Format.formatter, unit, unit) format4 -> 'a + +val process_warnings : unit -> unit diff --git a/compiler/tests-compiler/gh1051.ml b/compiler/tests-compiler/gh1051.ml index 53cebd9e7e..ad0b17834d 100644 --- a/compiler/tests-compiler/gh1051.ml +++ b/compiler/tests-compiler/gh1051.ml @@ -25,7 +25,7 @@ let%expect_test _ = Util.compile_and_run ~werror:false prog; [%expect {| - Warning: integer overflow: native integer 0xffffffff (4294967295) truncated to 0xffffffff (-1); the generated code might be incorrect. + Warning [integer-overflow]: native integer 0xffffffff (4294967295) truncated to 0xffffffff (-1); the generated code might be incorrect. ffffffff |}]; () @@ -34,7 +34,7 @@ let%expect_test _ = Util.print_fun_decl (Util.compile_and_parse ~werror:false prog) None; [%expect {| - Warning: integer overflow: native integer 0xffffffff (4294967295) truncated to 0xffffffff (-1); the generated code might be incorrect. + Warning [integer-overflow]: native integer 0xffffffff (4294967295) truncated to 0xffffffff (-1); the generated code might be incorrect. function caml_call2(f, a0, a1){ return (f.l >= 0 ? f.l : f.l = f.length) === 2 ? f(a0, a1) diff --git a/compiler/tests-compiler/pbt/test_int31.ml b/compiler/tests-compiler/pbt/test_int31.ml index 37f72d354c..5d346907da 100644 --- a/compiler/tests-compiler/pbt/test_int31.ml +++ b/compiler/tests-compiler/pbt/test_int31.ml @@ -68,7 +68,7 @@ let%expect_test _ = let output = [%expect.output] in let expected = Format.sprintf - "Warning: integer overflow: int32 0x%lx (%ld) truncated to 0x%lx (%ld); the \ + "Warning [integer-overflow]: int32 0x%lx (%ld) truncated to 0x%lx (%ld); the \ generated code might be incorrect.@." i i @@ -86,7 +86,7 @@ let%expect_test _ = let output = [%expect.output] in let expected = Format.sprintf - "Warning: integer overflow: integer 0x%x (%d) truncated to 0x%lx (%ld); the \ + "Warning [integer-overflow]: integer 0x%x (%d) truncated to 0x%lx (%ld); the \ generated code might be incorrect.@." i i @@ -104,7 +104,7 @@ let%expect_test _ = let output = [%expect.output] in let expected = Format.sprintf - "Warning: integer overflow: native integer 0x%nx (%nd) truncated to 0x%lx (%ld); \ + "Warning [integer-overflow]: native integer 0x%nx (%nd) truncated to 0x%lx (%ld); \ the generated code might be incorrect.@." i i diff --git a/compiler/tests-dynlink-js/dune b/compiler/tests-dynlink-js/dune index 0e1f614bb4..cef978f92d 100644 --- a/compiler/tests-dynlink-js/dune +++ b/compiler/tests-dynlink-js/dune @@ -24,6 +24,8 @@ %{bin:js_of_ocaml} --linkall %{read-strings:effects_flags.txt} + -w + no-missing-effects-backend -o %{target} %{dep:main.bc}))) diff --git a/compiler/tests-dynlink/dune b/compiler/tests-dynlink/dune index cd9b8a03fb..aa5ed08c50 100644 --- a/compiler/tests-dynlink/dune +++ b/compiler/tests-dynlink/dune @@ -14,6 +14,8 @@ --export export --pretty + -w + no-missing-effects-backend -o %{target} %{dep:main.bc}))) diff --git a/compiler/tests-toplevel/dune b/compiler/tests-toplevel/dune index f3eb01f5fb..b9c698ebb1 100644 --- a/compiler/tests-toplevel/dune +++ b/compiler/tests-toplevel/dune @@ -10,7 +10,14 @@ (rule (targets test_toplevel.js) (action - (run %{bin:js_of_ocaml} --toplevel %{dep:test_toplevel.bc} -o %{targets}))) + (run + %{bin:js_of_ocaml} + --toplevel + -w + no-missing-effects-backend + %{dep:test_toplevel.bc} + -o + %{targets}))) (rule (target test_toplevel.bc.js.actual) diff --git a/dune-workspace b/dune-workspace index 42991f68f8..46e434ee35 100644 --- a/dune-workspace +++ b/dune-workspace @@ -8,4 +8,6 @@ (js_of_ocaml ;; enable for debugging ;; (flags (:standard --debug stats-debug --debug invariant)) + (flags (:standard -w "no-missing-effects-backend")) + (link_flags (:standard -w "no-missing-effects-backend")) (runtest_alias runtest-js)))) \ No newline at end of file diff --git a/toplevel/bin/jsoo_common.ml b/toplevel/bin/jsoo_common.ml index 2b3a75de36..257ea1f772 100644 --- a/toplevel/bin/jsoo_common.ml +++ b/toplevel/bin/jsoo_common.ml @@ -52,12 +52,12 @@ let read_cmi ~dir cmi = with Not_found -> ( match cmi with (* HACK: here a list of known "hidden" cmi from the OCaml distribution. *) - | "dynlink_config.cmi" - | "dynlink_types.cmi" - | "dynlink_platform_intf.cmi" - | "dynlink_common.cmi" - | "dynlink_symtable.cmi" - | "dynlink_compilerlibs.cmi" -> raise Not_found + | "Dynlink_config.cmi" + | "Dynlink_types.cmi" + | "Dynlink_platform_intf.cmi" + | "Dynlink_common.cmi" + | "Dynlink_symtable.cmi" + | "Dynlink_compilerlibs.cmi" -> raise Not_found | cmi -> Format.eprintf "Could not find cmi %s or %s in %s@." diff --git a/toplevel/examples/eval/dune b/toplevel/examples/eval/dune index a9f4c6827d..d21bb5628f 100644 --- a/toplevel/examples/eval/dune +++ b/toplevel/examples/eval/dune @@ -22,6 +22,8 @@ %{dep:export.txt} --toplevel --pretty + -w + no-missing-effects-backend %{dep:eval.bc} -o %{targets}))) diff --git a/toplevel/test/dune b/toplevel/test/dune index 13fa263adf..ece87b1f3e 100644 --- a/toplevel/test/dune +++ b/toplevel/test/dune @@ -27,6 +27,8 @@ --toplevel --disable shortvar + -w + no-missing-effects-backend %{dep:test_toplevel1.bc} -o %{targets}))) @@ -40,6 +42,8 @@ --no-cmis --disable shortvar + -w + no-missing-effects-backend %{dep:test_toplevel2.bc} -o %{targets})))