package dunolint

  1. Overview
  2. Docs
A linter for build files in dune projects

Install

dune-project
 Dependency

Authors

Maintainers

Sources

dunolint-0.0.20251006.tbz
sha256=1b064927c9e1ef5352a1886ae34a206fef0ce6a913c19a77b0162acc108e0e50
sha512=6cbc08ba318bef6584d15a4491e3dde1bf436109ce0f8b7c400a9f91bbcee64c5785bc924df11eafe98243ec2f188a7f92c58c5062729f3e2af1e9977f1a5e67

doc/src/dunolint.dune_project_linter/dune_lang_version.ml.html

Source file dune_lang_version.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
(*********************************************************************************)
(*  Dunolint - A tool to lint and help manage files in dune projects             *)
(*  Copyright (C) 2024-2025 Mathieu Barbin <mathieu.barbin@gmail.com>            *)
(*                                                                               *)
(*  This file is part of Dunolint.                                               *)
(*                                                                               *)
(*  Dunolint is free software; you can redistribute it and/or modify it          *)
(*  under the terms of the GNU Lesser General Public License as published by     *)
(*  the Free Software Foundation either version 3 of the License, or any later   *)
(*  version, with the LGPL-3.0 Linking Exception.                                *)
(*                                                                               *)
(*  Dunolint is distributed in the hope that it will be useful, but WITHOUT      *)
(*  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or        *)
(*  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License  *)
(*  and the file `NOTICE.md` at the root of this repository for more details.    *)
(*                                                                               *)
(*  You should have received a copy of the GNU Lesser General Public License     *)
(*  and the LGPL-3.0 Linking Exception along with this library. If not, see      *)
(*  <http://www.gnu.org/licenses/> and <https://spdx.org>, respectively.         *)
(*********************************************************************************)

let field_name = "lang"

type t = { mutable dune_lang_version : Dune_project.Dune_lang_version.t }
[@@deriving sexp_of]

let create ~dune_lang_version = { dune_lang_version }
let dune_lang_version t = t.dune_lang_version
let set_dune_lang_version t ~dune_lang_version = t.dune_lang_version <- dune_lang_version

let read ~sexps_rewriter ~field =
  match field with
  | Sexp.List [ Sexp.Atom "lang"; Sexp.Atom "dune"; (Sexp.Atom version_string as atom) ]
    ->
    (* Parse version string like "3.17" into tuple (3, 17) *)
    (match String.split version_string ~on:'.' with
     | [ major_str; minor_str ] ->
       (match Int.of_string major_str, Int.of_string minor_str with
        | major, minor ->
          { dune_lang_version = Dune_project.Dune_lang_version.create (major, minor) }
        | exception _ ->
          Err.raise
            ~loc:(Sexps_rewriter.loc sexps_rewriter atom)
            [ Pp.textf "Invalid version format: %S." version_string ])
     | _ ->
       Err.raise
         ~loc:(Sexps_rewriter.loc sexps_rewriter atom)
         [ Pp.textf "Expected VERSION.MINOR format, got: %S." version_string ])
  | _ ->
    Err.raise
      ~loc:(Sexps_rewriter.loc sexps_rewriter field)
      [ Pp.text "Expected (lang dune VERSION) format." ]
;;

let write t =
  let version_string = Dune_project.Dune_lang_version.to_string t.dune_lang_version in
  Sexp.List [ Sexp.Atom "lang"; Sexp.Atom "dune"; Sexp.Atom version_string ]
;;

let rewrite t ~sexps_rewriter ~field =
  let new_field = write t in
  Dunolinter.Sexp_handler.replace_field ~sexps_rewriter ~field ~new_field
;;

type predicate = Dune_project.Dune_lang_version.Predicate.t

let eval t ~predicate =
  (match (predicate : predicate) with
   | `equals version -> Dune_project.Dune_lang_version.equal version t.dune_lang_version
   | `greater_than_or_equal_to version ->
     Dune_project.Dune_lang_version.compare t.dune_lang_version version >= 0
   | `less_than_or_equal_to version ->
     Dune_project.Dune_lang_version.compare t.dune_lang_version version <= 0)
  |> Dunolint.Trilang.const
;;

let enforce =
  Dunolinter.Linter.enforce
    (module Dune_project.Dune_lang_version.Predicate)
    ~eval
    ~enforce:(fun t predicate ->
      match predicate with
      | Not (`equals _) -> Eval
      | Not (`greater_than_or_equal_to _) -> Eval
      | Not (`less_than_or_equal_to _) -> Eval
      | T (`equals version) ->
        t.dune_lang_version <- version;
        Ok
      | T (`greater_than_or_equal_to version) ->
        if Dune_project.Dune_lang_version.compare t.dune_lang_version version < 0
        then t.dune_lang_version <- version;
        Ok
      | T (`less_than_or_equal_to version) ->
        if Dune_project.Dune_lang_version.compare t.dune_lang_version version > 0
        then t.dune_lang_version <- version;
        Ok)
;;

module Top = struct
  type nonrec t = t

  let eval = eval
  let enforce = enforce
end

module Linter = struct
  type t = Top.t
  type predicate = Dune_project.Predicate.t

  let eval (t : t) ~predicate =
    match (predicate : Dune_project.Predicate.t) with
    | `dune_lang_version condition ->
      Dunolint.Trilang.eval condition ~f:(fun predicate -> Top.eval t ~predicate)
    | predicate ->
      let () =
        (* This construct is the same as featuring all values in the match case
           but we cannot disable individual coverage in or patterns with
           bisect_ppx atm. Left for future work. *)
        match[@coverage off] predicate with
        | `dune_lang_version _ -> assert false
        | `name _ | `generate_opam_files _ | `implicit_transitive_deps _ -> ()
      in
      Dunolint.Trilang.Undefined
  ;;

  let enforce =
    Dunolinter.Linter.enforce
      (module Dune_project.Predicate)
      ~eval
      ~enforce:(fun t predicate ->
        match predicate with
        | Not _ -> Eval
        | T condition ->
          (match condition with
           | `dune_lang_version condition ->
             Top.enforce t ~condition;
             Ok
           | condition ->
             let () =
               match[@coverage off] condition with
               | `dune_lang_version _ -> assert false
               | `name _ | `generate_opam_files _ | `implicit_transitive_deps _ -> ()
             in
             Unapplicable))
  ;;
end