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.

merlint: fix E900/E905 rules + display Code Generation category

E900/E905 now walk the filesystem directly (like E800 interop rules)
instead of using Dune.libraries, fixing the false-negative where
project-scoped rules didn't find Wire users.

E905 now only flags .mli files in packages that actually use Wire.*,
eliminating false positives on merlint/dune.mli and prune/module_alias.mli.

Added "Interop Testing" and "Code Generation" to the display category
list in main.ml (was hardcoded to 6 categories).

Current violations:
- E900: 18 packages use Wire.Codec without c/ directory
- E905: 25 Wire struct_/module_/c_stubs/ml_stubs exposed in .mli

+39 -20
+39 -20
lib/rules/e905.ml
··· 22 22 then 23 23 let lib_dir = Filename.concat pkg_dir "lib" in 24 24 if Sys.file_exists lib_dir && Sys.is_directory lib_dir then 25 - let mli_files = 26 - try_readdir lib_dir 27 - |> List.filter (fun f -> Filename.check_suffix f ".mli") 25 + let all_files = try_readdir lib_dir in 26 + (* Only flag packages that actually use Wire *) 27 + let has_wire = 28 + List.exists 29 + (fun f -> 30 + Filename.check_suffix f ".ml" 31 + && (not (Filename.check_suffix f ".mli")) 32 + && 33 + try 34 + let c = 35 + In_channel.with_open_text 36 + (Filename.concat lib_dir f) 37 + In_channel.input_all 38 + in 39 + Astring.String.is_infix ~affix:"Wire." c 40 + with _ -> false) 41 + all_files 28 42 in 29 - List.iter 30 - (fun f -> 31 - try 32 - let path = Filename.concat lib_dir f in 33 - let content = 34 - In_channel.with_open_text path In_channel.input_all 35 - in 36 - List.iter 37 - (fun sym -> 38 - let pattern = "val " ^ sym in 39 - if Astring.String.is_infix ~affix:pattern content then 40 - issues := 41 - Issue.v { file = Filename.concat pkg f; symbol = sym } 42 - :: !issues) 43 - wire_symbols 44 - with _ -> ()) 45 - mli_files) 43 + if has_wire then 44 + let mli_files = 45 + List.filter (fun f -> Filename.check_suffix f ".mli") all_files 46 + in 47 + List.iter 48 + (fun f -> 49 + try 50 + let path = Filename.concat lib_dir f in 51 + let content = 52 + In_channel.with_open_text path In_channel.input_all 53 + in 54 + List.iter 55 + (fun sym -> 56 + let pattern = "val " ^ sym in 57 + if Astring.String.is_infix ~affix:pattern content then 58 + issues := 59 + Issue.v 60 + { file = Filename.concat pkg f; symbol = sym } 61 + :: !issues) 62 + wire_symbols 63 + with _ -> ()) 64 + mli_files) 46 65 packages; 47 66 !issues 48 67