19
19
(* <http://www.gnu.org/licenses/> and <https://spdx.org>, respectively. *)
20
20
(* ********************************************************************************)
21
21
22
- let maybe_autoformat_file ~previous_contents ~new_contents =
23
- (* For the time being we are using here a heuristic to drive whether to
24
- autoformat linted files. This is motivated by pragmatic reasoning and lower
25
- friction for onboarding in various situation where formatting may or may
26
- not be used in projects. *)
27
- if String. equal previous_contents new_contents
28
- then new_contents
29
- else (
30
- let was_originally_well_formatted =
31
- try
32
- let formatted =
33
- Dunolint_engine. format_dune_file ~new_contents: previous_contents
34
- in
35
- String. equal formatted previous_contents
36
- with
37
- | _ -> false
38
- in
39
- if was_originally_well_formatted
40
- then Dunolint_engine. format_dune_file ~new_contents
41
- else new_contents)
42
- ;;
43
-
44
- module Visitor_decision = struct
45
- (* A subtype of [Dunolint_engine.Visitor_decision] used by [Lint_file]. *)
46
- type t =
47
- | Continue
48
- | Skip_subtree
49
- end
50
-
51
- module Lint_file (Linter : Dunolinter.S ) = struct
52
- exception Skip_subtree
53
-
54
- let lint_stanza ~rules ~stanza =
55
- let loc =
56
- Sexps_rewriter. loc
57
- (Dunolinter. sexps_rewriter stanza)
58
- (Dunolinter. original_sexp stanza)
59
- in
60
- Dunolinter.Handler. emit_error_and_resume () ~loc ~f: (fun () ->
61
- match Dunolinter. linter stanza with
62
- | Unhandled -> ()
63
- | T { eval; enforce } ->
64
- List. iter rules ~f: (fun rule ->
65
- match Dunolint.Rule. eval rule ~f: eval with
66
- | `return -> ()
67
- | `enforce condition -> enforce condition
68
- | `skip_subtree -> raise Skip_subtree ))
69
- ;;
70
-
71
- let lint_file ~dunolint_engine ~rules ~(path : Relative_path.t ) =
72
- let previous_contents_ref = ref " " in
73
- let visitor_decision = ref Visitor_decision. Continue in
74
- Dunolint_engine. lint_file
75
- dunolint_engine
76
- ~path
77
- ?create_file:None
78
- ~rewrite_file: (fun ~previous_contents ->
79
- previous_contents_ref := previous_contents;
80
- match Linter. create ~path ~original_contents: previous_contents with
81
- | Error { loc; message } ->
82
- Err. error ~loc [ Pp. textf " %s" message ];
83
- previous_contents
84
- | Ok linter ->
85
- let () =
86
- try Linter. visit linter ~f: (fun stanza -> lint_stanza ~rules ~stanza ) with
87
- | Skip_subtree -> visitor_decision := Skip_subtree
88
- in
89
- Linter. contents linter)
90
- ~autoformat_file: (fun ~new_contents ->
91
- let previous_contents = ! previous_contents_ref in
92
- maybe_autoformat_file ~previous_contents ~new_contents );
93
- ! visitor_decision
94
- ;;
95
- end
96
-
97
- module Dune_lint = Lint_file (Dune_linter )
98
- module Dune_project_lint = Lint_file (Dune_project_linter )
99
-
100
- let visit_directory ~dunolint_engine ~config ~parent_dir ~files =
101
- match
102
- match Dunolint.Config. skip_subtree config with
103
- | None -> `return
104
- | Some condition ->
105
- Dunolint.Rule. eval condition ~f: (fun (`path condition ) ->
106
- Dunolinter. eval_path ~path: parent_dir ~condition )
107
- with
108
- | `enforce nothing -> Nothing. unreachable_code nothing [@ coverage off]
109
- | `skip_subtree -> Dunolint_engine.Visitor_decision. Skip_subtree
110
- | `return ->
111
- let rules = Dunolint.Config. rules config in
112
- let rec loop = function
113
- | [] -> Dunolint_engine.Visitor_decision. Continue
114
- | file :: files ->
115
- let path = Relative_path. extend parent_dir (Fsegment. v file) in
116
- (match
117
- match Dunolint.Linted_file_kind. of_string file with
118
- | Error (`Msg _ ) -> Visitor_decision. Continue
119
- | Ok linted_file_kind ->
120
- (match linted_file_kind with
121
- | `dune -> Dune_lint. lint_file ~dunolint_engine ~rules ~path
122
- | `dune_project -> Dune_project_lint. lint_file ~dunolint_engine ~rules ~path )
123
- with
124
- | Continue -> loop files
125
- | Skip_subtree -> Dunolint_engine.Visitor_decision. Skip_subtree )
126
- in
127
- loop files
128
- ;;
129
-
130
22
let main =
131
23
Command. make
132
24
~summary: " lint project"
133
25
(let % map_open.Command dunolint_engine_config = Dunolint_engine.Config. arg
134
26
and () = Log_cli. set_config ()
135
27
and config =
136
28
Arg. named_opt [ " config" ] Param. file ~doc: " Path to dunolint config file"
137
- and below = Common . below ~doc: " Lint only below this path"
29
+ and below = Common_helpers . below ~doc: " Lint only below this path"
138
30
and enforce =
139
31
Arg. named_multi
140
32
[ " enforce" ]
141
- (Common . sexpable_param (module Dunolint. Condition ))
33
+ (Common_helpers . sexpable_param (module Dunolint. Condition ))
142
34
~docv: " COND"
143
35
~doc: " Add condition to enforce"
144
36
>> | List. map ~f: (fun condition -> `enforce condition)
@@ -149,7 +41,10 @@ let main =
149
41
let contents = In_channel. read_all config in
150
42
Parsexp.Conv_single. parse_string_exn contents Dunolint.Config. t_of_sexp
151
43
| None ->
152
- Dunolint.Config. create ~skip_subtree: (Common. skip_subtree ~globs: [] ) ~rules: [] ()
44
+ Dunolint.Config. create
45
+ ~skip_subtree: (Common_helpers. skip_subtree ~globs: [] )
46
+ ~rules: []
47
+ ()
153
48
in
154
49
let config =
155
50
Dunolint.Config. create
@@ -163,5 +58,5 @@ let main =
163
58
dunolint_engine
164
59
?below
165
60
~f: (fun ~parent_dir ~subdirectories :_ ~files ->
166
- visit_directory ~dunolint_engine ~config ~parent_dir ~files ))
61
+ Linter. visit_directory ~dunolint_engine ~config ~parent_dir ~files ))
167
62
;;
0 commit comments