From 982dd5cc5a65ee97be05b1e936c7acf0dbd7e5b4 Mon Sep 17 00:00:00 2001
From: Patrick Baudin <patrick.baudin@cea.fr>
Date: Mon, 27 Feb 2023 11:01:40 +0100
Subject: [PATCH] [Lint] allows to overload ocp-indent commands as for the
 other known tools

---
 tools/lint/README.md | 83 ++++++++++++++++++++++++++++++++++++++++++++
 tools/lint/lint.ml   | 44 +++++++++++++++--------
 2 files changed, 112 insertions(+), 15 deletions(-)
 create mode 100644 tools/lint/README.md

diff --git a/tools/lint/README.md b/tools/lint/README.md
new file mode 100644
index 00000000000..b5ab4cfe030
--- /dev/null
+++ b/tools/lint/README.md
@@ -0,0 +1,83 @@
+## frama-c-lint tool
+
+## Usage
+
+
+```
+> frama-c-lint -help
+Usage: git ls-files -z | git check-attr --stdin -z -a | _build/install/default/bin/frama-c-lint [options]
+
+Checks or updates files in relation to lint constraints specified by these git attributes:
+  check-eoleof, check-syntax, check-utf8 and check-indent.
+
+Options:
+  --version        Prints tool version
+  -c <json-config-file> Reads the JSON configuration file (allows to overload the default configuration)
+  -e               Prints default JSON configuration
+  -s               Considers warnings as errors for the exit value
+  -u               Update ill-formed files (does not handle UTF8 update)
+  -v               Verbose mode
+  -help            Display this list of options
+  --help           Display this list of options
+```
+
+## Managed Git Attributes
+
+The tool manage the following `git` attributes:
+`check-eoleof`, `check-syntax`, `check-utf8`, `check-indent`.
+
+All of them can be `set` or `unset`, but a tool name can also be assignable to the `check-indent` attribute.
+
+The check/update commands related to `check-eoleof`, `check-syntax` and `check-utf8` attributes are not overloadable and cannot be parametrized.
+Only the commands related to `check-indent` attribute can be parametrized.
+
+## The Parametrizable Configuration
+
+The command `frama-c-lint -e` pretty prints the JSON description equivalent to the default parametrizable configuration related to `check-indent` attribute.
+
+```
+> frama-c-lint -e
+Default JSON configuration:
+[
+  {
+    "kind": "C",
+    "extensions": [ ".c", ".h" ],
+    "name": "clang-format",
+    "available_cmd": "clang-format --version > /dev/null 2> /dev/null",
+    "check_cmd": "clang-format --dry-run -Werror",
+    "update_cmd": "clang-format -i"
+  },
+  {
+    "kind": "Python",
+    "extensions": [ ".py" ],
+    "name": "black",
+    "available_cmd": "black --version > /dev/null 2> /dev/null",
+    "check_cmd": "black --quiet --line-length 100 --check",
+    "update_cmd": "black --quiet --line-length 100"
+  }
+]
+```
+
+This description defines `black` and `clang-format` as the assignable values of the `git` attribute `check-indent`. It also specifies the system command to use for checking the availability of the tool to use for checking/updating the indentation. The check and update commands are also specified.
+
+When the `check-indent` attribute is set without a value, the description specifies the tool to use from the extension of the file to check/update.
+
+There is also a built-in configuration for `.ml` and `.mli` files based of `ocp-indent` tool (from a directly use of `ocp-indent` library to improve the efficiency of the tool) that can be overloaded if necessary.
+That means there is an implicit overloadable JSON description:
+```
+[
+  {
+    "kind": "OCaml",
+    "extensions": [ ".ml", ".mli" ],
+    "name": "ocp-indent",
+    "available_cmd": "...",
+    "check_cmd": "...",
+    "update_cmd": "..."
+  }
+]
+```
+
+The option  `-c <json-confi g-file>` allows to extend and/or overload the default configuration.
+
+When the `available_cmd` field is set to an empty string, that disable the check/update with the related tool.
+An empty string can also be set to the field `check_cmd` (resp. `update_cmd`) when the related tool does not offer check (resp. update) command.
diff --git a/tools/lint/lint.ml b/tools/lint/lint.ml
index 919aadb4b4a..e08be5eb782 100644
--- a/tools/lint/lint.ml
+++ b/tools/lint/lint.ml
@@ -134,7 +134,7 @@ let parse_config config_file =
     in match config_tools with
     | Result.Ok external_tools -> updates_tbl external_tools
     | Result.Error txt ->
-      warn "Parse error:%s:%s" config_file txt
+      warn "Parse error:%s:%s@." config_file txt
 
 (************************)
 
@@ -145,13 +145,15 @@ type indent_check = NoCheck | Check of indent_formatter option
 let parse_indent_formatter ~file ~attr ~value = match value with
   | "unset" -> NoCheck
   | "set"   -> Check None (* use the default formatter *)
-  | "ocp-indent" -> Check (Some ml_indent_formatter)
   | _ ->
     match Hashtbl.find_opt external_tbl value with
     | None ->
-      warn "Unsupported indent formatter: %s %s=%s@."
-        file attr value;
-      NoCheck
+      if value = "ocp-indent" then
+        (* "ocp-indent" is not overloaded: using the built-in configuration *)
+        Check (Some ml_indent_formatter)
+      else (warn "Unsupported indent formatter: %s %s=%s@."
+              file attr value;
+            NoCheck)
     | res -> Check res
 
 (**************************************************************************)
@@ -175,7 +177,7 @@ let add_attr ~file ~attr ~value checks =
   let is_set = function
     | "set" -> true
     | "unset" -> false
-    | _ -> warn "Invalid attribute value: %s %s=%s" file attr value ; false
+    | _ -> warn "Invalid attribute value: %s %s=%s@." file attr value ; false
   in
   match attr with
   | "check-eoleof" -> { checks with eoleof = is_set value }
@@ -183,7 +185,7 @@ let add_attr ~file ~attr ~value checks =
   | "check-utf8"   -> { checks with utf8 = is_set value }
   | "check-indent" -> { checks with
                         indent = parse_indent_formatter ~file ~attr ~value }
-  | _ -> warn "Unknown attribute: %s %s=%s" file attr value;
+  | _ -> warn "Unknown attribute: %s %s=%s@." file attr value;
     checks
 
 let handled_attr s =
@@ -344,11 +346,12 @@ let check_indent ~indent_formatter ~update file =
   let tool = match indent_formatter with
     | Some tool -> tool
     | None -> (* uses the default formatter *)
-      match Filename.extension file with
-      | ".ml" | ".mli" -> ml_indent_formatter
-      | extension -> match Hashtbl.find_opt default_tbl extension with
-        | None -> raise Bad_ext
-        | Some tool -> tool
+      let extension = Filename.extension file in
+      match Hashtbl.find_opt default_tbl extension with
+      | Some tool -> tool
+      | None -> match extension with
+        | ".ml" | ".mli" -> ml_indent_formatter
+        | _ -> raise Bad_ext
   in match tool with
   | Ocp_indent -> check_ml_indent ~update file
   | Tool indent_formatter ->
@@ -419,6 +422,13 @@ let check ~verbose ~update file params =
 (* Options *)
 
 let exec_name = Sys.argv.(0)
+
+let version= "1.0"
+
+let version () =
+  Format.printf "%s version %s@." (Filename.basename exec_name) version;
+  exit 0
+
 let update = ref false
 let verbose = ref false
 let config_file = ref ""
@@ -428,8 +438,9 @@ let argspec = [
   "-u", Arg.Set update, " Update ill-formed files (does not handle UTF8 update)" ;
   "-v", Arg.Set verbose, " Verbose mode" ;
   "-s", Arg.Set strict, " Considers warnings as errors for the exit value" ;
-  "-c", Arg.String (fun s -> config_file := s), "<config-file> Reads the JSON configuration file (allows to overload the default configuration)" ;
-  "-e", Arg.Set extract_config, " Print default JSON configuration" ;
+  "-c", Arg.String (fun s -> config_file := s), "<json-config-file> Reads the JSON configuration file (allows to overload the default configuration)" ;
+  "-e", Arg.Set extract_config, " Prints default JSON configuration" ;
+  "--version", Arg.Unit version, " Prints tool version" ;
 ]
 let sort argspec =
   List.sort (fun (name1, _, _) (name2, _, _) -> String.compare name1 name2)
@@ -442,7 +453,10 @@ let () =
   Arg.parse
     (Arg.align (sort argspec))
     (fun s -> warn "Unknown argument: %s@." s)
-    ("Usage: git ls-files -z | git check-attr --stdin -z -a | " ^ exec_name ^ " [options]");
+    ("Usage: git ls-files -z | git check-attr --stdin -z -a | " ^ exec_name ^ " [options]\n"
+     ^"\nChecks or updates files in relation to lint constraints specified by these git attributes:\n"
+     ^"  check-eoleof, check-syntax, check-utf8 and check-indent.\n"
+     ^"\nOptions:");
   if !extract_config then
     Format.printf "Default JSON configuration:@.%a@."
       (Yojson.Safe.pretty_print ~std:false) (tools_to_yojson external_formatters)
-- 
GitLab