Opinionated OCaml linter with Merlin integration for code quality, naming conventions, and style checks
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

json: rename mem -> member / finish -> seal across the codec + value API

Object combinators: [Object.mem] -> [Object.member], [Object.opt_mem]
-> [Object.opt_member], [Object.case_mem] -> [Object.case_member]. The
sibling submodules [Object.Mem] / [Object.Mems] become
[Object.Member] / [Object.Members]. RFC 8259 §4 calls these
"name/value pairs, referred to as the members", so mirror the spec
name rather than the shortened [mem].

[Object.finish] -> [Object.seal]. "Seal" reads as "close the map, no
more members added", which is what the operation does.

Value constructors/queries: [Value.mem] (function) -> [Value.member];
[Value.mem_find] -> [Value.member_key]; [Value.mem_names] ->
[Value.member_names]; [Value.mem_keys] -> [Value.member_keys].
[type mem = ...] -> [type member = ...]; [type object'] still points
at [member list].

Downstream (~80 files across slack, sbom, stripe, sigstore, requests,
claude, irmin, freebox) updated via perl-pie. dune build clean,
dune test ocaml-json clean.

+129 -1
+7
docs/index.html
··· 1385 1385 </div> 1386 1386 <div class="error-hint"><p>Create &lt;package&gt;/dune containing (env (dev (flags :standard %{dune-warnings}))), and bump &lt;package&gt;/dune-project to (lang dune 3.21) or newer. This mirrors the workspace-root dune so that a standalone opam build of the package still enforces strict warnings under the dev profile. Reference: alcobar/dune.</p></div> 1387 1387 </div> 1388 + <div class="error-card" id="E526"> 1389 + <div> 1390 + <span class="error-code">E526</span> 1391 + <span class="error-title">Package dune-project must disable implicit transitive deps</span> 1392 + </div> 1393 + <div class="error-hint"><p>Add (implicit_transitive_deps false) to &lt;package&gt;/dune-project (or (implicit_transitive_deps false-if-hidden-includes-supported) if you need to keep compatibility with OCaml &lt; 5.2). Then audit each (libraries ...) clause to list any transitive deps the package actually uses directly. This makes (re_export ...) meaningful again and prevents deps from leaking into downstream opam depends via META requires.</p></div> 1394 + </div> 1388 1395 <h2 id="testing">Testing</h2> 1389 1396 <div class="category">E600-E699 • Test coverage and test quality issues</div> 1390 1397 <div class="error-card" id="E600">
+2 -1
dune-project
··· 30 30 opam 31 31 tty 32 32 vlog 33 - (alcotest :with-test))) 33 + (alcotest :with-test) 34 + loc))
+1
lib/data.ml
··· 41 41 E523.rule; 42 42 E524.rule; 43 43 E525.rule; 44 + E526.rule; 44 45 E600.rule; 45 46 E605.rule; 46 47 E606.rule;
+118
lib/rules/e526.ml
··· 1 + (** E526: Package dune-project must disable implicit transitive deps. 2 + 3 + With [(implicit_transitive_deps true)] (the dune default), all transitive 4 + library dependencies are visible at compile time. That makes 5 + [(re_export foo)] meaningless for visibility and quietly propagates deps 6 + into downstream META [requires], which leaks into consumers' opam depends. 7 + 8 + Each package [dune-project] must declare 9 + 10 + {v (implicit_transitive_deps false) v} 11 + 12 + or the graceful-fallback variant on older OCaml: 13 + 14 + {v (implicit_transitive_deps false-if-hidden-includes-supported) v} 15 + 16 + so only libraries listed in [(libraries ...)] are in scope, and [re_export] 17 + regains its intended role as the explicit opt-in for forwarding a dep to 18 + consumers. 19 + 20 + {b How to fix:} 21 + - add [(implicit_transitive_deps false)] to [<package>/dune-project], and 22 + - audit each [(libraries ...)] clause to list any previously transitive deps 23 + the package actually uses. *) 24 + 25 + type kind = Missing | Set_to_true 26 + type payload = { package : string; kind : kind } 27 + 28 + let try_readdir d = try Sys.readdir d |> Array.to_list with Sys_error _ -> [] 29 + 30 + let has_opam_file pkg_dir = 31 + List.exists 32 + (fun name -> Filename.check_suffix name ".opam") 33 + (try_readdir pkg_dir) 34 + 35 + let read_file path = 36 + try 37 + let ic = open_in path in 38 + let n = in_channel_length ic in 39 + let s = really_input_string ic n in 40 + close_in ic; 41 + Some s 42 + with Sys_error _ -> None 43 + 44 + let stanza_re = 45 + Re.compile 46 + (Re.seq 47 + [ 48 + Re.char '('; 49 + Re.rep Re.space; 50 + Re.str "implicit_transitive_deps"; 51 + Re.rep1 Re.space; 52 + Re.group 53 + (Re.alt 54 + [ 55 + Re.str "false-if-hidden-includes-supported"; 56 + Re.str "false"; 57 + Re.str "true"; 58 + ]); 59 + Re.rep Re.space; 60 + Re.char ')'; 61 + ]) 62 + 63 + let find_setting contents = 64 + match Re.exec_opt stanza_re contents with 65 + | None -> None 66 + | Some g -> Some (Re.Group.get g 1) 67 + 68 + let check (ctx : Context.project) = 69 + let root = ctx.project_root in 70 + let is_dir p = try Sys.is_directory p with Sys_error _ -> false in 71 + List.concat_map 72 + (fun name -> 73 + if 74 + name = "_build" || name = "_opam" || name = ".git" 75 + || String.starts_with ~prefix:"." name 76 + then [] 77 + else 78 + let pkg_dir = Filename.concat root name in 79 + if (not (is_dir pkg_dir)) || not (has_opam_file pkg_dir) then [] 80 + else 81 + let dp_path = Filename.concat pkg_dir "dune-project" in 82 + match read_file dp_path with 83 + | None -> [] 84 + | Some c -> ( 85 + match find_setting c with 86 + | Some "false" | Some "false-if-hidden-includes-supported" -> [] 87 + | Some "true" -> 88 + [ Issue.v { package = name; kind = Set_to_true } ] 89 + | Some _ | None -> [ Issue.v { package = name; kind = Missing } ])) 90 + (try_readdir root) 91 + 92 + let pp ppf { package; kind } = 93 + match kind with 94 + | Missing -> 95 + Fmt.pf ppf 96 + "%s/dune-project is missing (implicit_transitive_deps false); \ 97 + transitive deps leak into downstream META requires and pollute \ 98 + consumers' opam depends" 99 + package 100 + | Set_to_true -> 101 + Fmt.pf ppf 102 + "%s/dune-project sets (implicit_transitive_deps true); change to false \ 103 + (or false-if-hidden-includes-supported) to keep transitive deps \ 104 + scoped to the library that needs them" 105 + package 106 + 107 + let rule = 108 + Rule.v ~code:"E526" 109 + ~title:"Package dune-project must disable implicit transitive deps" 110 + ~category:Rule.Project_structure 111 + ~hint: 112 + "Add (implicit_transitive_deps false) to <package>/dune-project (or \ 113 + (implicit_transitive_deps false-if-hidden-includes-supported) if you \ 114 + need to keep compatibility with OCaml < 5.2). Then audit each \ 115 + (libraries ...) clause to list any transitive deps the package actually \ 116 + uses directly. This makes (re_export ...) meaningful again and prevents \ 117 + deps from leaking into downstream opam depends via META requires." 118 + ~examples:[] ~pp (Project check)
+1
merlint.opam
··· 27 27 "tty" 28 28 "vlog" 29 29 "alcotest" {with-test} 30 + "loc" 30 31 "odoc" {with-doc} 31 32 ] 32 33 build: [