diff --git a/infer/src/backend/infer.ml b/infer/src/backend/infer.ml index fe169a5ec..0cc500fca 100644 --- a/infer/src/backend/infer.ml +++ b/infer/src/backend/infer.ml @@ -77,6 +77,12 @@ let setup_results_dir () = -> assert_results_dir "please run an infer analysis first" let () = + ( if Config.linters_validate_syntax_only then + match CTLParserHelper.validate_al_files () with + | Ok () + -> exit 0 + | Error e + -> print_endline e ; exit 3 ) ; if Config.print_builtins then Builtin.print_and_exit () ; setup_results_dir () ; if Config.debug_mode then L.progress "Logs in %s@." (Config.results_dir ^/ Config.log_file) ; diff --git a/infer/src/base/Config.ml b/infer/src/base/Config.ml index 4478437d7..dfe148b3a 100644 --- a/infer/src/base/Config.ml +++ b/infer/src/base/Config.ml @@ -1252,6 +1252,12 @@ and linters_ignore_clang_failures = ~in_help:CLOpt.([(Capture, manual_clang_linters)]) ~default:false "Continue linting files even if some compilation fails." +and linters_validate_syntax_only = + CLOpt.mk_bool ~long:"linters-validate-syntax-only" + ~in_help:CLOpt.([(Capture, manual_clang_linters)]) + ~default:false + "Validate syntax of AL files, then emit possible errors in JSON format to stdout" + and load_average = CLOpt.mk_float_opt ~long:"load-average" ~short:'l' ~in_help:CLOpt.([(Capture, manual_generic)]) @@ -2065,6 +2071,8 @@ and linters_developer_mode = !linters_developer_mode and linters_ignore_clang_failures = !linters_ignore_clang_failures +and linters_validate_syntax_only = !linters_validate_syntax_only + and liveness = !liveness and load_average = diff --git a/infer/src/base/Config.mli b/infer/src/base/Config.mli index 22b5a96b8..a1cad9f90 100644 --- a/infer/src/base/Config.mli +++ b/infer/src/base/Config.mli @@ -475,6 +475,8 @@ val linters_developer_mode : bool val linters_ignore_clang_failures : bool +val linters_validate_syntax_only : bool + val liveness : bool val load_analysis_results : string option diff --git a/infer/src/clang/CTLExceptions.ml b/infer/src/clang/CTLExceptions.ml index b6cc1902f..ee7ddf239 100644 --- a/infer/src/clang/CTLExceptions.ml +++ b/infer/src/clang/CTLExceptions.ml @@ -20,6 +20,12 @@ let create_exc_info description lexbuf = let pos = lexbuf.Lexing.lex_curr_p in {description; filename= pos.pos_fname; line= pos.pos_lnum} +let json_of_exc_info exc_info = + `Assoc + [ ("description", `String exc_info.description) + ; ("filename", `String exc_info.filename) + ; ("line", `Int exc_info.line) ] + let () = Caml.Printexc.register_printer (fun exc -> match exc with diff --git a/infer/src/clang/CTLExceptions.mli b/infer/src/clang/CTLExceptions.mli index 8da2794a4..b19138b23 100644 --- a/infer/src/clang/CTLExceptions.mli +++ b/infer/src/clang/CTLExceptions.mli @@ -21,3 +21,5 @@ val create_exc_info : string -> Lexing.lexbuf -> exc_info val hum_string_of_exc_info : exc_info -> string (** human-readable version of exc_info *) + +val json_of_exc_info : exc_info -> Yojson.Basic.json diff --git a/infer/src/clang/CTLParserHelper.ml b/infer/src/clang/CTLParserHelper.ml new file mode 100644 index 000000000..7a790c726 --- /dev/null +++ b/infer/src/clang/CTLParserHelper.ml @@ -0,0 +1,37 @@ +(* + * Copyright (c) 2017 - present Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + *) + +open! IStd +open Ctl_lexer +open Lexing + +let parse_al_file fname channel = + let parse_with_error lexbuf = + try Some (Ctl_parser.al_file token lexbuf) with + | CTLExceptions.ALParserInvariantViolationException s + -> raise CTLExceptions.(ALFileException (create_exc_info s lexbuf)) + | SyntaxError _ | Ctl_parser.Error + -> raise CTLExceptions.(ALFileException (create_exc_info "SYNTAX ERROR" lexbuf)) + in + let lexbuf = Lexing.from_channel channel in + lexbuf.lex_curr_p <- {lexbuf.lex_curr_p with pos_fname= fname} ; + parse_with_error lexbuf + +let validate_al_files () = + let validate_al_file fname = + try + Utils.with_file_in ~f:(parse_al_file fname) fname |> ignore ; + None + with CTLExceptions.ALFileException exc_info -> Some (CTLExceptions.json_of_exc_info exc_info) + in + match List.filter_map ~f:validate_al_file Config.linters_def_file with + | [] + -> Ok () + | _ as errors + -> Error (Yojson.Basic.to_string (`List errors)) diff --git a/infer/src/clang/CTLParserHelper.mli b/infer/src/clang/CTLParserHelper.mli new file mode 100644 index 000000000..a718dd578 --- /dev/null +++ b/infer/src/clang/CTLParserHelper.mli @@ -0,0 +1,14 @@ +(* + * Copyright (c) 2017 - present Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + *) + +open! IStd + +val parse_al_file : string -> In_channel.t -> CTL.al_file option + +val validate_al_files : unit -> (unit, string) Result.t diff --git a/infer/src/clang/cFrontend_checkers_main.ml b/infer/src/clang/cFrontend_checkers_main.ml index d22e20d94..42207b342 100644 --- a/infer/src/clang/cFrontend_checkers_main.ml +++ b/infer/src/clang/cFrontend_checkers_main.ml @@ -8,29 +8,15 @@ *) open! IStd -open Lexing -open Ctl_lexer module L = Logging -let parse_al_file fname channel : CTL.al_file option = - let parse_with_error lexbuf = - try Some (Ctl_parser.al_file token lexbuf) with - | CTLExceptions.ALParserInvariantViolationException s - -> raise CTLExceptions.(ALFileException (create_exc_info s lexbuf)) - | SyntaxError _ | Ctl_parser.Error - -> raise CTLExceptions.(ALFileException (create_exc_info "SYNTAX ERROR" lexbuf)) - in - let lexbuf = Lexing.from_channel channel in - lexbuf.lex_curr_p <- {lexbuf.lex_curr_p with pos_fname= fname} ; - parse_with_error lexbuf - let already_imported_files = ref [] let rec parse_import_file import_file channel = if List.mem ~equal:String.equal !already_imported_files import_file then failwith ("Cyclic imports: file '" ^ import_file ^ "' was already imported.") else - match parse_al_file import_file channel with + match CTLParserHelper.parse_al_file import_file channel with | Some { import_files= imports ; global_macros= curr_file_macros @@ -63,7 +49,7 @@ and parse_imports imports_files = List.fold_right ~f:parse_one_import_file ~init:([], []) imports_files let parse_ctl_file linters_def_file channel : CFrontend_errors.linter list = - match parse_al_file linters_def_file channel with + match CTLParserHelper.parse_al_file linters_def_file channel with | Some { import_files= imports ; global_macros= curr_file_macros diff --git a/infer/src/clang_stubs/CTLParserHelper.ml b/infer/src/clang_stubs/CTLParserHelper.ml new file mode 100644 index 000000000..30a4f3fc5 --- /dev/null +++ b/infer/src/clang_stubs/CTLParserHelper.ml @@ -0,0 +1,10 @@ +(* + * Copyright (c) 2017 - present Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + *) + +let validate_al_files () = prerr_endline "ERROR: infer was built without clang support." ; exit 1 diff --git a/infer/src/clang_stubs/CTLParserHelper.mli b/infer/src/clang_stubs/CTLParserHelper.mli new file mode 100644 index 000000000..8f3381e87 --- /dev/null +++ b/infer/src/clang_stubs/CTLParserHelper.mli @@ -0,0 +1,12 @@ +(* + * Copyright (c) 2016 - present Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + *) + +open! IStd + +val validate_al_files : unit -> (unit, string) Result.t