Skip to content
Snippets Groups Projects
Commit 31a3817b authored by Patrick Baudin's avatar Patrick Baudin
Browse files

Merge branch 'feature/patrick/configurable-indent-formatter' into 'master'

[lint]  supports more indent formatters

See merge request frama-c/frama-c!4039
parents 975c9cfd 99f00124
No related branches found
No related tags found
No related merge requests found
...@@ -47,6 +47,13 @@ Changelog merge=union ...@@ -47,6 +47,13 @@ Changelog merge=union
# note: "check-syntax" includes already "check-eoleof" # note: "check-syntax" includes already "check-eoleof"
*.ml check-syntax check-indent -check-eoleof *.ml check-syntax check-indent -check-eoleof
*.mli check-syntax check-indent -check-eoleof *.mli check-syntax check-indent -check-eoleof
*.py check-indent check-eoleof
# linting exceptions
share/analysis-scripts/benchmark_database.py -check-indent
share/analysis-scripts/summary.py -check-indent
share/analysis-scripts/results_display.py -check-indent
src/plugins/e-acsl/examples/ensuresec/**/*.py -check-indent
## Unset "-check-eoleof" ## Unset "-check-eoleof"
......
[tool.black]
line-length = 100
include = '\.py$'
extend-exclude = '''
# If you modify this list of files, run "black -v ." to confirm which files are
# properly excluded.
benchmark_database.py
| frama_c_results.py
| git_utils.py
| results_display.py
| summary.py
'''
...@@ -320,7 +320,6 @@ ...@@ -320,7 +320,6 @@
(analysis-scripts/fc_stubs.c as lib/analysis-scripts/fc_stubs.c) (analysis-scripts/fc_stubs.c as lib/analysis-scripts/fc_stubs.c)
(analysis-scripts/list_functions.ml as lib/analysis-scripts/list_functions.ml) (analysis-scripts/list_functions.ml as lib/analysis-scripts/list_functions.ml)
(analysis-scripts/prologue.mk as lib/analysis-scripts/prologue.mk) (analysis-scripts/prologue.mk as lib/analysis-scripts/prologue.mk)
(analysis-scripts/pyproject.toml as lib/analysis-scripts/pyproject.toml)
(analysis-scripts/readme-graph.graphml as lib/analysis-scripts/readme-graph.graphml) (analysis-scripts/readme-graph.graphml as lib/analysis-scripts/readme-graph.graphml)
(analysis-scripts/readme-graph.svg as lib/analysis-scripts/readme-graph.svg) (analysis-scripts/readme-graph.svg as lib/analysis-scripts/readme-graph.svg)
(analysis-scripts/README.md as lib/analysis-scripts/README.md) (analysis-scripts/README.md as lib/analysis-scripts/README.md)
......
#!/usr/bin/env python3 #!/usr/bin/env python3
#-*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Perform some sanity checks related to the compliance JSON files # Perform some sanity checks related to the compliance JSON files
...@@ -70,5 +70,7 @@ for (i, v) in c11_funs.items(): ...@@ -70,5 +70,7 @@ for (i, v) in c11_funs.items():
print(f"warning: C11 function {i} is not a function in POSIX, but a {id_type}") print(f"warning: C11 function {i} is not a function in POSIX, but a {id_type}")
posix_header = posix_dict[i]["header"] posix_header = posix_dict[i]["header"]
if header != posix_header: if header != posix_header:
sys.exit(f"error: C11 function {i} mapped to header {header}, but in POSIX it is mapped to header {posix_header}") sys.exit(
f"error: C11 function {i} mapped to header {header}, but in POSIX it is mapped to header {posix_header}"
)
print(f"{c11_funs_path.name} checked.") print(f"{c11_funs_path.name} checked.")
...@@ -22,6 +22,23 @@ ...@@ -22,6 +22,23 @@
open CamomileLibrary open CamomileLibrary
(**************************************************************************)
(* Warning/Error *)
let strict = ref false
let res = ref true (* impact the exit value *)
let warn ftext =
if !strict then
res := false ;
Format.eprintf "Warning: ";
Format.eprintf ftext
let error ftext =
res := false ;
Format.eprintf ftext
(**************************************************************************) (**************************************************************************)
(* Utils *) (* Utils *)
...@@ -49,31 +66,83 @@ let lines_from_in channel = ...@@ -49,31 +66,83 @@ let lines_from_in channel =
let acc = lines_from_buffer [] content 0 in let acc = lines_from_buffer [] content 0 in
List.rev acc List.rev acc
(**************************************************************************)
(* Supported indent formatter *)
type formatter_cmds =
{ mutable is_available: bool option ;
kind: string ;
name: string ;
available_cmd: string ;
check_cmd: string ;
update_cmd: string (* leaves it empty if there is no updating command *)
}
let c_indent_formatter =
{ is_available = None ;
kind = "C";
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"
}
let python_indent_formatter =
{ is_available = None ;
kind = "Python";
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"
}
type indent_formatter = Ocp_indent | Tool of formatter_cmds
let ml_indent_formatter = Ocp_indent
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)
| "clang-format" -> Check (Some (Tool c_indent_formatter))
| "black" -> Check (Some (Tool python_indent_formatter))
| _ -> warn "Unsupported indent formatter: %s %s=%s@."
file attr value;
NoCheck
(**************************************************************************) (**************************************************************************)
(* Available Checks and corresponding attributes *) (* Available Checks and corresponding attributes *)
type checks = type checks =
{ eoleof : bool { eoleof : bool
; indent : bool ; indent : indent_check
; syntax : bool ; syntax : bool
; utf8 : bool ; utf8 : bool
} }
let no_checks = let no_checks =
{ eoleof = false { eoleof = false
; indent = false ; indent = NoCheck
; syntax = false ; syntax = false
; utf8 = false ; utf8 = false
} }
let add_attr checks attr value = let add_attr ~file ~attr ~value checks =
let value = value = "set" in let is_set = function
| "set" -> true
| "unset" -> false
| _ -> warn "Invalid attribute value: %s %s=%s" file attr value ; false
in
match attr with match attr with
| "check-eoleof" -> { checks with eoleof = value } | "check-eoleof" -> { checks with eoleof = is_set value }
| "check-indent" -> { checks with indent = value } | "check-syntax" -> { checks with syntax = is_set value }
| "check-syntax" -> { checks with syntax = value } | "check-utf8" -> { checks with utf8 = is_set value }
| "check-utf8" -> { checks with utf8 = value } | "check-indent" -> { checks with
| _ -> failwith (Format.sprintf "Unknown attr %s" attr) indent = parse_indent_formatter ~file ~attr ~value }
| _ -> warn "Unknown attribute: %s %s=%s" file attr value;
checks
let handled_attr s = let handled_attr s =
s = "check-eoleof" || s = "check-indent" || s = "check-eoleof" || s = "check-indent" ||
...@@ -96,10 +165,11 @@ let rec collect = function ...@@ -96,10 +165,11 @@ let rec collect = function
collect tl collect tl
| file :: attr :: value :: tl -> | file :: attr :: value :: tl ->
let checks = get file in let checks = get file in
Hashtbl.replace table file (add_attr checks attr value) ; Hashtbl.replace table file (add_attr ~file ~attr ~value checks) ;
collect tl collect tl
| [] -> () | [] -> ()
| l -> List.iter (Format.eprintf "Could not load file list %s@.") l | [ file ; attr ] -> warn "Missing attribute value: %s %s=?@." file attr
| [ file ] -> warn "Missing attribute name for file: %s@." file
(**************************************************************************) (**************************************************************************)
(* Functions used to check lint *) (* Functions used to check lint *)
...@@ -184,7 +254,7 @@ let config () = ...@@ -184,7 +254,7 @@ let config () =
(fun stx -> (fun stx ->
try Approx_lexer.enable_extension stx try Approx_lexer.enable_extension stx
with IndentExtend.Syntax_not_found name -> with IndentExtend.Syntax_not_found name ->
Format.eprintf "Warning: unknown syntax extension %S@." name) warn "Unknown syntax extension %S@." name)
syntaxes ; syntaxes ;
global_config := Some config ; global_config := Some config ;
config config
...@@ -219,47 +289,52 @@ let check_ml_indent ~update file = ...@@ -219,47 +289,52 @@ let check_ml_indent ~update file =
(* C/H *) (* C/H *)
let clang_format_available = ref None let is_formatter_available ~file indent_formatter =
let clang_format_available () = match indent_formatter.is_available with
match !clang_format_available with
| None -> | None ->
clang_format_available := let is_available = (0 = Sys.command indent_formatter.available_cmd) in
Some (0 = Sys.command "clang-format --version > /dev/null") ; indent_formatter.is_available <- Some is_available ;
Option.get !clang_format_available if not is_available then
| Some available -> available warn "%s is unavailable for checking indentation of some %s files (i.e. %s)@."
indent_formatter.name indent_formatter.kind file;
let check_c_indent ~update file = is_available
if not @@ clang_format_available () then true | Some is_available -> is_available
else
let opt = if update then "-i" else "--dry-run -Werror" in
0 = Sys.command (Format.sprintf "clang-format %s \"%s\"" opt file)
exception Bad_ext exception Bad_ext
let check_indent ~update file = let check_indent ~indent_formatter ~update file =
match Filename.extension file with let tool = match indent_formatter with
| ".c" | ".h" -> check_c_indent ~update file | Some tool -> tool
| ".ml" | ".mli" -> check_ml_indent ~update file | None -> (* uses the default formatter *)
| _ -> raise Bad_ext match Filename.extension file with
| ".c" | ".h" -> Tool c_indent_formatter
let res = ref true | ".ml" | ".mli" -> ml_indent_formatter
| ".py" -> Tool python_indent_formatter
| _ -> raise Bad_ext
in match tool with
| Ocp_indent -> check_ml_indent ~update file
| Tool indent_formatter ->
if not @@ is_formatter_available ~file indent_formatter then true
else if not update then
0 = Sys.command (Format.sprintf "%s \"%s\"" indent_formatter.check_cmd file)
else if indent_formatter.update_cmd <> "" then
0 = Sys.command (Format.sprintf "%s \"%s\"" indent_formatter.update_cmd file)
else true (* there no updating command *)
(* Main checks *) (* Main checks *)
let check ~verbose ~update file params = let check ~verbose ~update file params =
if verbose then if verbose then
Format.printf "Checking %s@." file ; Format.printf "Checking %s@." file ;
if Sys.is_directory file then () if Sys .is_directory file then ()
else begin else begin
let in_chan = open_in file in let in_chan = open_in file in
let content = read_buffered in_chan in let content = read_buffered in_chan in
close_in in_chan ; close_in in_chan ;
(* UTF8 *) (* UTF8 *)
if params.utf8 then if params.utf8 then
if not @@ is_utf8 content then begin if not @@ is_utf8 content then
Format.eprintf "Bad encoding (not UTF8) for %s@." file ; error "Bad encoding (not UTF8) for %s@." file ;
res := false
end ;
(* Blanks *) (* Blanks *)
let rewrite = ref false in let rewrite = ref false in
let syntactic_check checker content message = let syntactic_check checker content message =
...@@ -267,8 +342,8 @@ let check ~verbose ~update file params = ...@@ -267,8 +342,8 @@ let check ~verbose ~update file params =
if update && not was_ok if update && not was_ok
then begin rewrite := true ; new_content end then begin rewrite := true ; new_content end
else if not was_ok then begin else if not was_ok then begin
Format.eprintf "%s for %s@." message file ; error "%s for %s@." message file ;
res := false ; new_content new_content
end end
else new_content else new_content
in in
...@@ -289,14 +364,15 @@ let check ~verbose ~update file params = ...@@ -289,14 +364,15 @@ let check ~verbose ~update file params =
end ; end ;
(* Indentation *) (* Indentation *)
try try
if params.indent then begin
if not @@ check_indent ~update file then begin match params.indent with
Format.eprintf "Bad indentation for %s@." file ; | NoCheck -> ()
res := false | Check indent_formatter ->
end ; if not @@ check_indent ~indent_formatter ~update file then
error "Bad indentation for %s@." file ;
end ;
with Bad_ext -> with Bad_ext ->
Format.eprintf "Don't know how to (check) indent %s@." file ; error "Don't know how to (check) indent %s@." file
res := false
end end
(**************************************************************************) (**************************************************************************)
...@@ -309,6 +385,7 @@ let verbose = ref false ...@@ -309,6 +385,7 @@ let verbose = ref false
let argspec = [ let argspec = [
"-u", Arg.Set update, " update ill-formed files (does not handle UTF8 update)" ; "-u", Arg.Set update, " update ill-formed files (does not handle UTF8 update)" ;
"-v", Arg.Set verbose, " verbose mode" ; "-v", Arg.Set verbose, " verbose mode" ;
"-s", Arg.Set strict, " considers warnings as errors for the exit value" ;
] ]
let sort argspec = let sort argspec =
List.sort (fun (name1, _, _) (name2, _, _) -> String.compare name1 name2) List.sort (fun (name1, _, _) (name2, _, _) -> String.compare name1 name2)
...@@ -318,11 +395,9 @@ let sort argspec = ...@@ -318,11 +395,9 @@ let sort argspec =
(* Main *) (* Main *)
let () = let () =
if not @@ clang_format_available () then
Format.eprintf "clang-format unavailable, I will not check C files@." ;
Arg.parse Arg.parse
(Arg.align (sort argspec)) (Arg.align (sort argspec))
(fun s -> Format.eprintf "Unknown argument: %s" s) (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]");
collect @@ lines_from_in stdin ; collect @@ lines_from_in stdin ;
Hashtbl.iter (check ~verbose:!verbose ~update:!update) table ; Hashtbl.iter (check ~verbose:!verbose ~update:!update) table ;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment